1. 程式人生 > 實用技巧 >2020牛客第十場 C D I

2020牛客第十場 C D I

比賽網址:https://ac.nowcoder.com/acm/contest/5675

C Decrement on the Tree

給一棵邊權非負的樹,規定一個操作(u,v)為將u到v路徑上所有邊邊權減1,求讓所有邊為0至少需要多少次操作。

一開始可能覺得毫無思路,但是硬著頭皮做下去,終於還是想出瞭解法。
不過我意識到了該學學set的用法了。set是不能處理相同的元素的,需要用mutiset,且刪除時用S.erase(S.find(x));

D Hearthstone Battlegrounds

一道模擬題,由於重要的是經驗而不是題目,就不放題面了。

這道題我一開始以為是O(1)的分類討論,我相信只要分得足夠細,是一定可以做到O(1)求出結果的。
但是分了幾個小時後,還是放棄了。
看了題解,又回頭看了資料範圍,總共才10^6,模擬就完了......三分鐘敲完程式碼AC......md
下次遇到困難,比如寫了半小時還沒做出來,不妨再仔細讀下題目,或許看錯題或是有另外方法了呢。

I Tournament

n個人兩兩比賽,每天一場,共n*(n+1)/2天。規定每個人停留的天數是他參加的第一場比賽到他參加的最後一場比賽。要求所有人停留時間之和最短,求比賽安排。

所有人的停留天數之和,等於每一天的停留人數之和。

而停留人數的變化是連續的,而且是單峰的。連續是指除了每天人數最多+2或-2;單峰是指人數必定先增加後減小,因為1個人想要離開,必須跟其他人比賽,這樣必然有一個從0到n的不下降區間,而一個人離開後不可能再回來,這樣必然有一個從n到0的不上升區間。

我們先從峰值考慮,如何讓有n個人的天數最少。很簡單,可以只有1天。
現在考慮如何讓有至少n-1個人的天數最少。只要先讓1號和2號與前n-2號人都比賽過,之後進行1:(n-1)、1:n、2:(n-1)、2:n這四場比賽就行了,一共4天。
同時可以看到,在保證至少有n-1個人的天數最少的前提下,可以實現有n個人的天數最少。
依此類推,我們得到了對於峰值的賽程安排:

.... 1 - (n-i+1)
...
|... 1 - (n-3)
|... 2 - (n-3)
|... 3 - (n-3)
||.. 1 - (n-2)
||.. 2 - (n-2)
|||. 1 - (n-1)
|||| 1 - n
|||. 2 - (n-1)
|||. 2 - n
||.. 3 - (n-2)
||.. 3 - (n-1)
||.. 3 - n
|... 4 - (n-3)
|... 4 - (n-2)
|... 4 - (n-1)
|... 4 - n
...
.... i - n

這裡需要保證 i<(n-i+1),解得 i<(n+1)/2,我們令mid=n/2。

這說明對於1mid和(n-mid+1)n之間得比賽如何安排,我們已經知道了。現在的問題是1~mid和n-mid+1內部的比賽如何安排。

我們依然延續那個思路:讓有至少3人的天數最少,讓有至少4人的天數最少......
有3人的天數至少為n(n+1)/2-2。有4人的天數至少為n(n+1)/2-6......過程我想我不用再詳寫了吧

這樣我們就得到了1mid和(n-mid+1)n內部的安排。但是如果n為奇數,mid與(n-mid+1)之間還有一個數:mid+1。
對mid+1號的安排依然遵循那個原則。

這樣,最優的賽程安排就完成了。


全部程式碼:

C

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std; 

const int N=1e5+10;
typedef long long LL;

struct Tri{
	int a, b, c;
	Tri(int a=0, int b=0, int c=0):a(a),b(b),c(c){}
};

vector<Tri> V;
set::multiset<LL> S[N];
LL sum[N], maxn[N];
int n, q;
LL cnt;

LL updata(int u){
	return maxn[u]*2ll>sum[u]?(2ll*maxn[u]-sum[u]):(sum[u]&1ll);
}

int main(){
	cin>>n>>q;
	for(int i=1, u, v, w; i<n; ++i){
		scanf("%d%d%d", &u, &v, &w);
		V.push_back(Tri(u,v,w));
		sum[u]+=w; sum[v]+=w;
		S[u].insert(-w); S[v].insert(-w);
	}
	for(int i=1; i<=n; ++i){
		maxn[i]=-*S[i].begin();
		cnt+=updata(i);
	}
	printf("%lld\n", cnt>>1ll);
	while(q--){
		int k, e;
		scanf("%d%d", &k, &e); k--;
		int u=V[k].a, v=V[k].b, w=V[k].c;
		V[k].c=e;
		S[u].erase(-w); S[v].erase(-w);
		S[u].insert(-e); S[v].insert(-e);
		cnt-=updata(u)+updata(v);
		sum[u]+=e-w; sum[v]+=e-w;
		maxn[u]=-(*S[u].begin()); maxn[v]=-*S[v].begin();
		cnt+=updata(u)+updata(v);
		printf("%lld\n", cnt>>1ll);
	}
}

D

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define A (a[0]+a[1]+a[2]+a[3])
#define A2 (a[0]+a[1]+a[2]+a[3])
#define B (b[0]+b[1]+b[2]+b[3])

int a[5], b[5];

int main(){
	int T; cin>>T;
	while(T--){
		for(int i=0; i<4; ++i) scanf("%d", a+i);
		for(int i=0; i<4; ++i) scanf("%d", b+i);
		a[4]=b[4]=0;
		
		while(A&&B){
			if(a[4]){
				a[4]--;
				if(b[0]){
					b[0]--; b[2]++;
					continue;
				}else if(b[1]){
					b[1]--; b[3]++;
					continue;
				}else a[4]++;
			}
			if(a[2]){
				a[2]--; a[4]++;
				if(b[2]){
					b[2]--; b[4]+=(A==0);
				}else if(b[3]){
					b[3]--;
				}else if(b[0]){
					b[0]--; b[2]++;
				}else if(b[1]){
					b[1]--; b[3]++;
				}
			}else if(a[0]){
				a[0]--; a[2]++;
				if(b[2]){
					b[2]--; b[4]+=(A==0);
				}else if(b[3]){
					b[3]--;
				}else if(b[0]){
					b[0]--; b[2]++;
				}else if(b[1]){
					b[1]--; b[3]++;
				}
			}else if(a[1]){
				a[1]--; a[3]++;
				if(b[2]){
					b[2]--; b[4]+=(A==0);
				}else if(b[3]){
					b[3]--;
				}else if(b[0]){
					b[0]--; b[2]++;
				}else if(b[1]){
					b[1]--; b[3]++;
				}
			}else if(a[3]){
				a[3]--;
				if(b[2]){
					b[2]--; b[4]+=(A==0);
				}else if(b[3]){
					b[3]--;
				}else if(b[0]){
					b[0]--; b[2]++;
				}else if(b[1]){
					b[1]--; b[3]++;
				}
			}
		}
		puts(A||(B==0&&a[4]>b[4])?"Yes":"No");
	}
	return 0;
}

I

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int main(){
	int T; cin>>T;
	while(T--){
		int n; cin>>n; int mid=n>>1;
		//對 1~mid內部的安排 
		for(int i=2; i<=mid; ++i) for(int j=1; j<i; ++j) printf("%d %d\n", j, i);
		//對 mid+1 的特判 
		if(n&1) for(int i=1; i<=mid; ++i) printf("%d %d\n", i, mid+1);
		//對 1~mid 與 (n-mid+1)~n 之間的安排 
		for(int i=n-mid+1; i<=n; ++i) for(int j=1; j<=n-i&&j<=mid; ++j) printf("%d %d\n", j, i);
		for(int i=1; i<=mid; ++i) for(int j=n-i+1; j<=n; ++j) printf("%d %d\n", i, j);
		//對 mid+1 的特判 
		if(n&1) for(int i=n-mid+1; i<=n; ++i) printf("%d %d\n", mid+1, i);
		//對 (n-mid+1)~n 內部的安排  
		for(int i=n-mid+1; i<=n; ++i) for(int j=i+1; j<=n; ++j) printf("%d %d\n", i, j);
	}
	return 0;
}