杭电多校2020第三场

1005-Little W and Contest

题意:
给定n个点,有两种点,权值分别为1和2,初始时,n个点互不相连。

接着会加入n−1条边,保证每次加入的边的两个端点事先是不相连通的。接着会加入n−1条边,保证每次加入的边的两个端点事先是不相连通的。

要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。

每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。

题解:

要使得三个点权值之和不少于5,有两种选取方案,分别是1、2、2 和 2、2、2.

那么,一开始的方案数sum = C(cnt2,2) * C(cnt1,1) + C(cnt2,3),cnt1表示权值为1的点的个数,cnt2表示权值为2的点的个数。

每次添加新的边,都会减少可行的方案数。利用并查集维护,设u,v为要连接的两个点,pu为u所在联通块的根节点,pv为v所在联通块的根节点,Gu表示u所在联通块,Gv表示v所在联通块,Gt表示剩下的所有点,p1[i]表示以i为根节点的联通块中权值为1的点的个数,p2[i]表示以i为根节点的联通块中权值为2的点的个数,那么,减少的方案数必须是从Gv,Gu,Gt各取一个点(为什么不从Gu取两个点?因为之前已经计算过这种取法,而且不满足三个人互相认识)
分四种情况:

(1) Gu 选取权值为2的点,Gv选取权值为2的点,Gt选取权值为1的点,那么不可行的方案数为

p2[pu] * p2[pv] * (cnt1 - p1[pu] - p1[pv])

(2) Gu 选取权值为2的点,Gv选取权值为2的点,Gt选取权值为2的点,那么不可行的方案数为

p2[pu] * p2[pv] * (cnt2 - p2[pu] - p2[pv])

(3) Gu 选取权值为2的点,Gv选取权值为1的点,Gt选取权值为2的点,那么不可行的方案数为

p2[pu] * p1[pv] * (cnt2 - p2[pu] - p2[pv])

(4) Gu 选取权值为1的点,Gv选取权值为2的点,Gt选取权值为2的点,那么不可行的方案数为

p1[pu] * p2[pv] * (cnt2 - p2[pu] - p2[pv])

AC_CODE:

ll a[maxn],father[maxn];
ll p1[maxn],p2[maxn];

void init(){
	for(int i=0; i<=maxn-2; ++i){
		father[i] = i;
	}
	MS0(p1); MS0(p2);
}

int get_father(int x){
	if(father[x] == x) return x;
	return father[x] = get_father(father[x]);
}

void merge(int x, int y){
	x = get_father(x);
	y = get_father(y);
	if(x != y){
		father[x] = y;
		p1[y] += p1[x];
		p2[y] += p2[x];
		p1[x] = 0;
		p2[x] = 0;
	}
}

void solve(){
	int n;
	R(n);
	init();
	int cnt1 = 0, cnt2 = 0;
	FOR(i,1,n) {
		R(a[i]);
		if(a[i] == 1) cnt1 += 1, p1[i] = 1, p2[i] = 0;
		else cnt2 += 1, p1[i] = 0, p2[i] = 1;
	}
	assert((cnt1+cnt2)==n);
	ll sum = (qmul(C(cnt2,2),C(cnt1,1)) + C(cnt2,3)) % mod;
	W(sum);
	FOR(i,2,n) {
		int u,v;
		R(u,v);
		int pu = get_father(u);
		int pv = get_father(v);
		sum = (sum - (p2[pu] * p2[pv] % mod) * (cnt1 - p1[pu] - p1[pv]) % mod + mod) % mod;
		sum = (sum - (p2[pu] * p2[pv] % mod) * (cnt2 - p2[pu] - p2[pv]) % mod + mod) % mod;
		sum = (sum - (p2[pu] * p1[pv] % mod) * (cnt2 - p2[pu] - p2[pv]) % mod + mod) % mod;
		sum = (sum - (p1[pu] * p2[pv] % mod) * (cnt2 - p2[pu] - p2[pv]) % mod + mod) % mod;
//		debug(pu,pv,p1[pu],p1[pv],p2[pu],p2[pv]);
		merge(u,v);
//		debug(pu,pv,p1[pu],p1[pv],p2[pu],p2[pv]);
		W(sum);
		assert(p1[v] <= cnt1);
		assert(p2[v] <= cnt2);
	}
}

Tokitsukaze and Rescue

题意:
给定一个完全图,n个点,n*(n-1)/2条双向边,删除k条边后使1到n的最短路最大,求最大值。

题解:
每一次删边的过程显然是删除最短路中某条边,暴力枚举最短路每条边,然后变成删除(k-1)条边的子问题,重复k次枚举,再计算1到n的最短路,维护最短路最大值即可。

AC_CODE:

const int maxn = 100;
VI G[maxn];
int d[maxn],dis[maxn][maxn],fa[maxn];
int ans,n,k;

void init() {
	for(int i=0; i<maxn-1; ++i) G[i].clear();
	ans = 0;
}

void dijsktra(int s) {
	priority_queue<PII, vector<PII>, greater<PII>> pq;
	FOR(i,0,maxn-2) d[i] = 1<<30;
	d[s] = 0;
	pq.push(MP(0,s));
	while(!pq.empty()) {
		PII temp = pq.top();
		pq.pop();
		int v = temp.S;
		if(d[v] < temp.F) continue;
		for(auto x: G[v]) {
			if(d[x] > d[v] + dis[x][v]) {
				d[x] = d[v] + dis[x][v];
				fa[x] = v;
				pq.push(MP(d[x],x));
			}
		}
	}
}

void dfs(int i) {
	dijsktra(1);
	if(i == k + 1) {
		ans = max(ans, d[n]);
		return;
	}
	int new_fa[maxn];
	FOR(i,1,n) new_fa[i] = fa[i];
	int cur = n;
	while(true) {
		int x = cur;
		int y = new_fa[cur];
		int temp = dis[x][y];
		dis[x][y] = dis[y][x] = 1<<30;
		dfs(i+1);
		dis[x][y] = dis[y][x] = temp;
		cur = new_fa[cur];
		if(cur == 1) break;
	}
}

void solve(){
	init();
	R(n,k);
	FOR(i,1,n*(n-1)/2) {
		int u,v,w;
		R(u,v,w);
		G[u].PB(v);
		G[v].PB(u);
		dis[u][v] = dis[v][u] = w;
	}
	dfs(1); 
	W(ans);
}

你可能感兴趣的:(杭电多校2020第三场)