CF2134游记

前言

这次比赛打炸了, C 题磕了好久没做出来,后面不想打了,在 \(0:00\)提前离场。
CF2134 \(Div.2\) 难度,场切 A,B 。

A.Painting With Two Colors

题面

\(t\) 组数据,每组数据:
给定 \(3\) 个整数 \(n,a,b\)
问你能否选择 \(2\) 个整数 \(x,y\)
\(n\) 个格子中第 \(x\)\(x+a-1,y\)\(y+b-1\) 个格子分别染上红色和蓝色,蓝色会覆盖红色。
问最后能否使得这 \(n\) 个格子染色情况对称。

思路

如果 \(a\le b\) 那么只需要考虑 \(b\) 即可
而对称的充要条件是 \(n-b\equiv 0(mod\) \(2)\)
我们只需要令 \(a_i\leftarrow a_i+(a_i\%(k+1))\times k\)

实现

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int tt,n,a,b;
void solve(){
	cin>>n>>a>>b;
	if(a>b){
	if(((n-a)&1)||((n-b)&1))puts("No");
	else puts("Yes");
	}
	else{
		if((n-b)&1)puts("No");
		else puts("Yes");
	}
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--)solve();
	return 0;
}

B.Add 0 or K

题面

\(t\) 组数据,每组数据:
给定一个大小为 \(n\) 的数组 \(a\) ,与一个整数 \(k\)
你可以进行至多 \(k\) 次操作:

  • 取一个数组 \(b\) ,其中 \(b_i\int\{0,k\}\)
  • \(a_i\leftarrow a_i+b_i\)
    问你是否能使得 \(gcd(a_1,...,a_n)>1\)

思路

我们注意到最终的 \(a_i\) 共有 \(k+1\) 种取值 \(A_i={a_i,a_i+k,a_i+2k,...a_i+k^2}\)
\(gcd(k,k+1)=1\) 所以 \(A_i\equiv{0,1,...,n}(mod\) \(k+1)\)
所以我们一定能在 \(k\) 次操作以内,使得 \(a_i\) 变为 \(k+1\) 的倍数。
\(k\equiv-1(mod\) \(k+1)\)

实现

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
const int N=1e5+5;
int tt,n;ll a[N],k;
void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;++i)cin>>a[i];
	if(n==1){
		cout<<a[1]+k<<endl;
	}
	else{
		for(int i=1;i<=n;++i)cout<<a[i]+(a[i]%(k+1))*k<<" ";
		cout<<endl;
	}
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--)solve();
	return 0;
}

C.Even Larger

题面

定义
对于一个数组 \(a\) ,若其所有大小不小于 \(2\) 的子数组 \(a[l,...,r]\) 都有:
\(\displaystyle \sum_{odd}a_i\le\sum_{even}a_i\)
则称其为可爱数组。

\(t\) 组数据,每组数据:
给定一个大小为 \(n\) 数组 \(a\) ,你可以进行若干次操作:

  • \(a_i>0\) ,令 \(a_i\leftarrow a_i-1\)
    问最少进行多少次操作可以使得 \(a\) 变为可爱数组。

思路

首先我们可以发现,我们只需要考虑大小为 \(2,3\) 的子数组,因为更大的子数组可以由若干个大小为 \(2,3\) 的数组组成。
若这些小的子数组为可爱的,则大的数组一定是可爱的。
\(n\) 为偶数,我们令 \(n\leftarrow n+1,a_n=0\)
对于所有下标 \(l,r\) 满足:
\(l,r\) 是奇数且 \(l+2=r\) ,如果满足 \(a[l,l+1,r]\)可爱的。
则有 \(a_{l+1}\ge a_l+a_r\)
进而 \(\displaystyle\sum_{even}a_i\ge\sum_{even}(a_{i-1}+a_{i+1})=2\sum_{odd}a_i-a_1-a_n\ge\sum_{odd}a_i\)
那么我我们只需要贪心地从左至右处理对应的 \(l,r\) 即可。

实现

#include<iostream>
using namespace std;
const int N=2e5+10;
int tt,n,a[N],b[N],mn;long long ans;
void solve(){
	cin>>n;
	ans=0;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		if(i&1)b[i]=0;
	}
	if(!(n&1)){
		++n;
		b[n]=a[n]=0;
	}
	for(int i=1;i<=n;i+=2){
		mn=a[i];
		if(i>=3)mn=min(mn,a[i-1]-b[i-2]);
		if(i<n)mn=min(a[i+1],mn);
		b[i]=mn;
		ans+=a[i]-mn;
	}
	cout<<ans<<endl;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--)solve();
	return 0;
}

D.Sliding Tree

题面

\(t\) 组数据,每组数据:
给定一棵大小为 \(n\) 的树,你需要进行若干次操作,使得这棵树变为一个链:

  • 选定 \(3\) 个整数 \(a,b,c\)
  • \(b\) 连接的所有非 \(b,c\) 节点连接到 \(c\) 上。

思路

引理1
对于任意路径 \(l:(u,v)\) 和任意操作 \(a,b,c\) ,路径 \(u,v\) 的长度 \(d(l)\) 会至多增加 \(1\)
证明:
\(b\notin l\)\(b\in\{u,v\}\)
显然 \(d(l)\) 不会变
否则若 \(b\in l\)
\(l\) 上与 \(b\) 相邻的两个点为 \(p,q\)

  1. \(a=p,c=q\) ,则 \(d(l)\) 不变;
  2. \(a=p,c\ne q\) ,则 \(d(l)+1\)
  3. \(a\ne p,c=q\) ,则 \(d(l)-1\)

所以 \(d(l)\) 至多增加 \(1\)

于是树上最长路径————直径也至多增加 \(1\)
那么我们是否能做到每次都令直径增加 \(1\) 呢?
引理1种的 \(2.\) 可知:
只需要找到一组点 \(a,b,c\) ,满足 \(a,b\)\(l\) 上而 \(c\) 不在 \(l\) 上即可。
这样的点显然存在,我们只需要输出任意一组。
求直径的两端点用一个 DFS 求出;
标记直径两端点再用一个 DFS ;
最后,使用 DFS 寻找上述点对即可。

实现

#include<iostream>
#include<vector>
using namespace std;
const int N=2e5+5;
int tt,n,dis[N],deg[N];
bool flg,mrk[N];
vector<int>g[N];
void dfs1(int u,int ft){
	for(auto v:g[u]){
		if(v==ft)continue;
		dis[v]=dis[u]+1;
		dfs1(v,u);
	}
}
void dfs2(int u,int ft){
	for(auto v:g[u]){
		if(v==ft)continue;
		dfs2(v,u);
		mrk[u]|=mrk[v];
	}
}
bool dfs3(int u,int ft){
	for(auto v:g[u]){
		if(v==ft)continue;
		if(!mrk[v]&&ft){
			cout<<ft<<" "<<u<<" "<<v<<endl;
			return 1;
		}
		if(mrk[v])if(dfs3(v,u))return 1;
	}
	return 0;
}
void solve(){int x,y;
	cin>>n;
	for(int i=1;i<=n;++i){
		deg[i]=mrk[i]=0;
		g[i].clear();
	}
	for(int i=1;i<n;++i){
		cin>>x>>y;
		++deg[x];++deg[y];
		g[x].push_back(y);
		g[y].push_back(x);
	}
	flg=1;
	for(int i=1;i<=n;++i)if(deg[i]>2){
		flg=0;break;
	}
	if(flg){
		cout<<"-1\n";
		return;
	}
	dis[1]=0;
	dfs1(1,0);
	y=1;
	for(int i=1;i<=n;++i)if(dis[i]>dis[y])y=i;
	x=y;
	dfs1(y,0);
	for(int i=1;i<=n;++i)if(dis[i]>dis[x])x=i;
	mrk[x]=1;
	dfs2(y,0);
	dfs3(y,0);
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

E.Power Boxes

题面

交互题
\(t\) 组数据,每组数据:
你需要确定一个未知的大小为 \(n\) 的数组 \(a\)
你可以进行至多 \(\left\lceil\frac{3n}{2}\right\rceil\) 次询问:

  1. \(swap\) \(i\) :交换 \(a_i\)\(a_{i+1}\)
  2. \(throw\) \(i\) :从 \(i\) 开始执行 \(i\leftarrow i+a_i\) 直至 \(i>n\) ,并返回执行次数 \(d_i\)

思路

不妨设 \(d_{n+1}=d_{n+2}=0\)
首先,若 \(d_{i+1}\ne d{i+2}\) 我们很容易确定 \(a_i\)
只需要询问 \(throw\) \(i\) 得到 \(d_{i}\) 即可;
否则 \(d_{i}=d_{i+1}+1\) 不需要询问。

引理1
已确定的 \(a_i\) 有至少 \(\left\lceil\frac{n}{2}\right\rceil\)
证明:
\(a_i\) 不确定,必有 \(d_{i}=d_{i+1}+1\)
那么 \(a_i-1\) 一定是确定的。
同时 \(d_{i+1}=d_{i+2}\) ,而 \(d_{i+1}=d_{i+1+a_i}+1\)
于是有 \(d_{i+2}=d_{i+1}=d_{i+2}+1\) ,即 \(a_{i+1}\) 也是确定的。
于是有: \(tot_{确定}+tot_{不确定}=n\)\(tot_{确定}\ge tot_{不确定}\)
\(tot_{确定}\ge\left\lceil\frac{n}{2}\right\rceil\)

所以对于不确定的 \(a_i\) 我们将其与 \(a_{i+1}\) 交换,
由于 \(d_{(i+1)+1}\ne d_{(i+1)+2}\) 我们可以确定交换过来的 \(a_i\)
对于 \(a_n\) 我们将其与 \(a_{n-1}\) 交换并询问 \(throw\) \(n-1\) 即可。
最终询问次数为 \(2\times tot{不确定}+tot_{确定}\le 3\times tot_{确定}\le \left\lceil\frac{3n}{2}\right\rceil\)

实现

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1005;
int tt,n,d[N],a[N];
void solve(){
	cin>>n;
	for(int i=1;i<=n;++i)a[i]=0;
	d[n]=1;d[n+1]=d[n+2]=0;
	for(int i=n-1;i>=1;--i){
		if(d[i+1]==d[i+2]){
			d[i]=d[i+1]+1;
		}
		else{
			cout<<"throw "<<i<<endl;
			cin>>d[i];
			if(d[i]==d[i+1]+1)a[i]=1;
			else a[i]=2;
		}
	}
	for(int i=1;i<=n;++i)if(!a[i]){
		if(i==n){
			cout<<"swap "<<n-1<<"\nthrow "<<n-1<<endl;
			cin>>a[n];
			if(a[n]==2)a[n]=1;
			else a[n]=2;
		}
		else{
			cout<<"swap "<< i <<"\nthrow "<<i+1<<endl;
			cin>>a[i];
			if(a[i]==d[i+1]+1)a[i]=1;----------------
			else a[i]=2;
		}
	}
	cout<<"! ";
	for(int i=1;i<=n;++i)cout<<a[i]<<" ";cout<<endl;
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

F.Permutation Oddness

题面

\(t\) 组数据,每组数据:
给定四个整数 \(c_0,c_1,c_2,c_3\)
现有一个大小为 \(n=\sum c_i\) 的数组 \(a\) ,定义其奇异度
\(\displaystyle\sum lowbit(a_i\oplus a_{i+1})\)
你的任务是求出从 \(0\)\(2(n-1)\) 中每一个整数 \(k\) 对应的数组 \(a\) 的个数。

思路

注意到 \(k\) 的范围为 \([0,2(n-1)]\) 于是 \(lowbit(a_i\oplus a_{i+1})\in[0,2]\)
考察 \(lowbit(a_i\oplus a_{i+1})\) 的可能情况:

(a_i,a_{i+1}) lowbit
(i,i) 0
(0,1) 1
(0,3) 1
(1,2) 1
(2,3) 1
(0,2) 2
(1,3) 2

于是我(题)们(解)发现:
我们将 \(0,2\) 分成一组 \(1,3\) 分成一组后,
组内 \(lowbit\in{0,2}\) ,组间 \(lowbit=1\) ,组数相差至多 \(1\)
定义 \(r_{i,j}\) 表示将 \(c_0\)\(0\)\(c_2\)\(2\) 分成 \(i\) 段,且段内贡献奇异度\(j\) 的方案数。
\(1,3\) 同理定义 \(b_{i,j}\)
我们考虑如何计算 \(r_{i,j}\)
\(0,2\) 被分成了 \(l\)\(0,2\) 的连续段, \(l_1,l_2\) 分别为其中 \(0,2\) 连续段个数。
那么这样的串的个数是 \(\begin{pmatrix}c_0-1\\l_1-1\end{pmatrix}\cdot\begin{pmatrix}c_2-1\\l_2-1\end{pmatrix}\)
假设我们在 \(l-1\) 个不同的 \((a_i,a_{i+1})\) 对中选择 \(k_1\) 个断开, \(c_1+c_2-l\) 个相同的 \((a_i,a_{i+1})\) 对中选择 \(k_2\) 个断开,
则有 \(\begin{pmatrix}l-1\\k_1\end{pmatrix}\cdot\begin{pmatrix}c_0+c_2-l\\k_2\end{pmatrix}\) 种短法。
于是 \(r_{k_1+k_2+1,2(l-1-k_1)}=\begin{pmatrix}c_0-1\\l_1-1\end{pmatrix}\cdot\begin{pmatrix}c_2-1\\l_2-1\end{pmatrix}\cdot\begin{pmatrix}l-1\\k_1\end{pmatrix}\cdot\begin{pmatrix}c_0+c_2-l\\k_2\end{pmatrix}\)
那么我们枚举所有可能的 \((l,k_1,k_2)\) 即可在 \(O(n^3)\) 的时间内算出 \(r_{i,j}\)
同理,在相同的时间内算出 \(b_{i,j}\)

实现

#include<iostream>
#include<vector>
using namespace std;
#define ll long long
const int N=805;
const ll mod=1e9+7;
int tt,c0,c1,c2,c3,n;
ll fac[N],inv[N];
ll qpow(ll a,ll b){
	ll ret=1;
	while(b){
		if(b&1)ret=ret*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ret;
}
ll C(ll n,ll m){
	return inv[n]*inv[m-n]%mod*fac[m]%mod;
}
void calc(int a,int b,vector<vector<ll> >&vec){
	int x,y,dif,sam;
	ll mul;
	for(int i=2;i<=a+b;++i){
		if(i&1)x=i/2,y=i-x;
		else x=y=i/2;
		dif=i-1;
		sam=a+b-1-dif;
		if(a>=x&&b>=y){
			mul=C(x-1,a-1)*C(y-1,b-1)%mod;
			for(int j=0;j<=dif;++j)
				for(int k=0;k<=sam;++k)
					(vec[j+k+1][dif-j]+=mul*C(j,dif)%mod*C(k,sam)%mod)%=mod;
		}
		if(a>=y&&b>=x){
			mul=C(y-1,a-1)*C(x-1,b-1)%mod;
			for(int j=0;j<=dif;++j)
				for(int k=0;k<=sam;++k)
					(vec[j+k+1][dif-j]+=mul*C(j,dif)%mod*C(k,sam)%mod)%=mod;
		}
	}
}
void solve(){
	cin>>c0>>c1>>c2>>c3;
	n=c0+c1+c2+c3;
	vector<vector<ll> > r(n+1,vector<ll>(n+1,0)),b(n+1,vector<ll>(n+1,0));
	calc(c0,c2,r);
	calc(c1,c3,b);
	vector<ll>ans(2*n-1,0);
	for(int i=1;i<=(n+1)/2;++i){
		for(int j=0;j<=n;++j)for(int k=0;k<=n;++k){
			int oddness=(i+j+k)*2-1;
			if(oddness>=0&&oddness<=2*(n-1)){
				(ans[oddness]+=r[i][j]*b[i][k]%mod*2%mod)%=mod;
			}
		}
	}
	for(int i=1;i<=(n+1)/2&&i+1<=n;++i){
		for(int j=0;j<=n;++j)for(int k=0;k<=n;++k){
			int oddness=(i+j+k)*2;
			if(oddness>=0&&oddness<=2*(n-1)){
				(ans[oddness]+=r[i][j]*b[i+1][k]%mod)%=mod;
				(ans[oddness]+=r[i+1][j]*b[i][k]%mod)%=mod;
			}
		}
	}
	for(int i=0;i<=2*n-2;++i)cout<<ans[i]<<' ';cout<<endl;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	fac[0]=inv[0]=1;
	for(int i=1;i<=800;++i)fac[i]=fac[i-1]*i%mod;
	inv[800]=qpow(fac[800],mod-2);
	for(int i=800;i>=2;--i)inv[i-1]=inv[i]*i%mod;
	cin>>tt;
	while(tt--)solve();
	return 0;
}
posted @ 2025-08-29 15:18  Xie2Yue  阅读(22)  评论(0)    收藏  举报