Codeforces Round 947 (Div. 1 + Div. 2)

image

image

中立的。感觉确实打少了就唐了。

A

卡了 \(8\) min 才过,乐。你考虑 \(x+y\rightarrow y+x\) 的操作其实就是把开头的一段一道末尾。如果操作大于一次,可以合并成一个操作。因此我们最多操作一次。直接复制两边判即可。

B

首先,最小的 \(a_x\)\(x\) 一定要选,否则后面无法补偿。因此我们已经确定了一个了。那么,我们把 \(\mod a_x=0\) 的全部去掉,最小的就是另一个答案。最后再判断一次。

C

wa 了 \(2\) 次,唐。你考虑到如果有两个相同的连续元素了,我们一定可以把这个序列全部变为这个元素。怎么出现两个相同元素呢?容易发现操作 \(>3\) 的长度的区间是不优的,一定可以拆成更多的小区间。所以只需要判断 \(2,3\) 长度的即可。

D

我们先考虑一下 A 和 B 可以集合到一个点的情况。这种情况我们设集合在 \(x\),我们造作次数就是 \(\max(dis(a,x),dis(b,x))+cal(x)\)\(cal(x)\) 的值是以 \(x\) 为根的树要遍历到每一个节点的最小造作步数,贪心容易发现是 \(2(n-1)-maxdep\)\(maxdep\) 是以 \(x\) 为根的最大深度。怎么选集合点呢?我们发现,我们集合点 \(x\) 移动一步,\(maxdep\) 最多 \(+1\),所以我们让 \(\max(dis(a,x),dis(b,x))\) 最小最好,因为移动不会更优。这个就是 \(a,b\) 路径的中点。

如果不能集合到一个点,即 \(a,b\) 路径长度为偶数个点。这个我们就移到它们刚刚距离为 \(1\) 即可,以 \(a\) 作为集合点,最后答案 \(+1\)

E

这下真的唐掉了。大家就看一个乐子。因为我的做法显然复杂度/程序长度上都不优。

考虑黑色节点形成一条链的条件。

  • 记自己是黑色的并且没有黑色的儿子的点的个数为 \(cnt\)\(cnt\) 显然合法,\(cnt>2\) 显然不合法。因此我们 \(cnt=2\),记这两个点为 \(x,y\)

  • \(x\)\(y\) 的路径上都是黑色,并且总的黑色数目等于路径上的数目。

要维护单点修改和查询路径和。显然可以用树剖。因此我们得到了 \(\mathcal{O}(n\log^2 n)\) 唐氏做法。

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 2e5+5;

ll n,m,w[N];
ll dfn[N],tot,top[N],fa[N],son[N],dep[N],siz[N],rnk[N],ff[N][22];
vector<int> g[N];
ll dat[N<<2],tag[N<<2];
void init(){
	tot=0;
	for (int i=1; i<=n; i++){
		dfn[i]=top[i]=fa[i]=son[i]=dep[i]=siz[i]=rnk[i]=0;
		g[i].clear();
		for (int j=0; j<22; j++){
			ff[i][j]=0;
		}
	}
	for (int i=1; i<=4*n+4; i++){
		dat[i]=tag[i]=0;
	}
}
void pu(int k){
	dat[k]=dat[k<<1]+dat[k<<1|1];
} 
void pd(int k,ll l,ll r){
	if (tag[k]){
		ll mid=l+r>>1;
		tag[k<<1]=tag[k<<1]+tag[k];
		tag[k<<1|1]=tag[k<<1|1]+tag[k];
		dat[k<<1]=dat[k<<1]+(mid-l+1)*tag[k];
		dat[k<<1|1]=dat[k<<1|1]+(r-mid)*tag[k];
		tag[k]=0;
	}
}
void bd(int k,int l,int r){
	tag[k]=0;
	if (l==r){
		dat[k]=w[rnk[l]];
		return;
	}
	int mid=l+r>>1;
	bd(k<<1,l,mid);
	bd(k<<1|1,mid+1,r);
	pu(k);
}
void upd(int k,int l,int r,int ql,int qr,ll ad){
	if (l>qr || r<ql){
		return;
	}
	if (ql<=l && r<=qr){
		tag[k]=tag[k]+ad;
		dat[k]=dat[k]+ad*(r-l+1);
		return;
	}
	pd(k,l,r);
	int mid=l+r>>1;
	upd(k<<1,l,mid,ql,qr,ad);
	upd(k<<1|1,mid+1,r,ql,qr,ad);
	pu(k);
}
ll qy(int k,int l,int r,int ql,int qr){
	if (l>qr || r<ql){
		return 0;
	}
	if (ql<=l && r<=qr){
		return dat[k];
	}
	pd(k,l,r);
	int mid=l+r>>1;
	ll vl=qy(k<<1,l,mid,ql,qr);
	ll vr=qy(k<<1|1,mid+1,r,ql,qr);
	return vl+vr;
}
void dfs1(int u,int fz){
	ll mx=0;
	siz[u]=1,fa[u]=fz,dep[u]=dep[fz]+1;
	ff[u][0]=fz;
	for (int i=1; i<22; i++){
		ff[u][i]=ff[ff[u][i-1]][i-1];
	}
	for (auto v : g[u]){
		if (v^fz){
			dfs1(v,u),siz[u]+=siz[v];
			if (siz[v]>mx){
				mx=siz[v],son[u]=v;
			}
		}
	}
}
void dfs2(int u,int tp){
	top[u]=tp,dfn[u]=++tot,rnk[tot]=u;
	if (!son[u]){
		return;
	}
	dfs2(son[u],tp);
	for (auto v : g[u]){
		if ((v^fa[u]) && (v^son[u])){
			dfs2(v,v);
		}
	}
}
int lca(int u,int v){
	if (dep[u]<dep[v]){
		swap(u,v);
	}
	int dif=dep[u]-dep[v];
	for (int i=0; i<20; i++){
		if (dif>>i&1){
			u=ff[u][i];
		}
	}
	if (u==v){
		return u;
	}
	for (int i=19; i>=0; i--){
		if (ff[u][i]!=ff[v][i]){
			u=ff[u][i],v=ff[v][i];
		}
	}
	return ff[u][0];
}
void upd_chain(int x,int y,ll z){
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]){
			swap(x,y);
		}
		upd(1,1,n,dfn[top[x]],dfn[x],z);
		x=fa[top[x]];
	}
	if (dep[x]>dep[y]){
		swap(x,y);
	}
	upd(1,1,n,dfn[x],dfn[y],z);
}
ll qy_chain(int x,int y){
	ll ans=0;
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]){
			swap(x,y);
		}
		ans+=qy(1,1,n,dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	if (dep[x]>dep[y]){
		swap(x,y);
	}
	ans+=qy(1,1,n,dfn[x],dfn[y]);
	return ans;
}

int snc[N],is[N];

void solve(){
	cin>>n>>m;
	init();
	int cnt11=0;
	for (int i=1; i<=n; i++){
		snc[i]=is[i]=0;
		cin>>w[i];
		cnt11+=w[i];
	}
	for (int i=1; i<n; i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs1(1,0);
	dfs2(1,1);
	bd(1,1,n);
	for (int i=1; i<=n; i++){
		if (w[i]){
			snc[fa[i]]++;
		}
	}
	int cnt=0;
	set<int> st;
	for (int i=1; i<=n; i++){
		if (w[i] && snc[i]==0){
			cnt++;
			st.insert(i);
			is[i]=1;
		}
	}
	while (m--){
		int u;
		cin>>u;
		upd_chain(u,u,w[u]==1?-1:1);
		cnt11+=(w[u]==1?-1:1);
		if (!w[u]){
			snc[fa[u]]++;
			if (is[fa[u]] && fa[u] && snc[fa[u]]==1){
				cnt--;
				st.erase(fa[u]);
				is[fa[u]]=0;
			}
			if (snc[u]==0){
				cnt++;
				st.insert(u);
				is[u]=1;
			}
		}
		else{
			snc[fa[u]]--;
		//	cout<<fa[u]<<endl;
			if (w[fa[u]] && fa[u] && snc[fa[u]]==0){
				cnt++;
				st.insert(fa[u]);
				is[fa[u]]=1;
			}
			if (is[u]){
				cnt--;
				st.erase(u);
				is[u]=0;
			}
		}
		//cout<<cnt<<"!"<<"\n";
		w[u]^=1;
		if (cnt==1){
			cout<<"Yes\n";
		}
		else{
			if (cnt==0 || cnt>2){
				cout<<"No\n";
			}
			else{
				int x=-1,y=-1;
				for (auto u : st){
					if (x==-1) x=u;
					else y=u; 
				}
				//cout<<x<<","<<y<<","<<lca(x,y)<<"\n";
				int cal=qy_chain(x,y);
				//cout<<cal<<"\n";
				if (cal==cnt11 && cal==dep[x]+dep[y]-dep[lca(x,y)]*2+1){
					cout<<"Yes\n";
				}
				else{
					cout<<"No\n";
				}
			}
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	int t;
	cin>>t;
	while (t--){
		solve();
	}
	return 0;
}

F

赛后补的。

我们一个一个位确定。考虑你已经确定了 \(x\) 位,那么你的限制还有 \(2^{n-x}\) 条。考虑下一位是 \(1\) 还是 \(0\)。设约束 \(c_1,c_2\)\(c_1\) 下一位是 \(1\)\(c_2\)\(0\)

  • 如果放 \(1\)\(c_1\) 可以用的就会减少 \(1\),产生新的约束 \(c=(c_1»1)\And c_2\)

  • 如果放 \(0\)\(c_1\) 可以用的不会减少,产生新的约束 \(c=c_1\And c_2\)

dfs 即可。时间复杂度 \(\mathcal{O}(n2^n)\)

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

int n;
int con[22][1<<21];
vector<int> ans;

void dfs(int s,int p){
	if (p==n){
		if (con[p][0]&1){
			ans.push_back(s);
		}
		return;
	}
	int ms=(1<<n-p-1);
	for (int i=0; i<ms; i++){
		con[p+1][i]=con[p][i]&con[p][i|ms]; 
	}
	dfs(s<<1,p+1);
	for (int i=0; i<ms; i++){
		con[p+1][i]=con[p][i]&(con[p][i|ms]>>1); 
	}
	dfs(s<<1|1,p+1);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	con[0][0]=1;
	for (int i=1; i<(1<<n); i++){
		cin>>con[0][i];
	}
	dfs(0,0);
	cout<<ans.size()<<"\n";
	for (auto u : ans){
		cout<<u<<"\n";
	}
	return 0;
}

posted @ 2024-05-27 18:28  SFlyer  阅读(136)  评论(0编辑  收藏  举报