Codeforces Round #754 (Div. 2)

Codeforces Round #754 (Div. 2)

Problem A A.M. Deviation

给出\(a_1,a_2,a_3\)\(1 \leq a_i \leq 10^8\),每次可以选取两个元素\(a_i,a_j\),将\(a_i\)自增\(1\),\(a_j\)自减\(1\).
询问若干次操作后\(|a_1+a_3-2a_2|\)的最小值。

按照操作,\(a_1+a_2+a_3\)的值是不变的,也就是说考虑若干次操作的效果相当于\(n=a_1+a_2+a_3\)的再分配。
\(n\)\(3\)的倍数的时候,将\(a_1=a_2=a_3=\frac{n}{3}\),可以让\(|a_1+a_3-2a_2|\)\(0\)显然是最小值。
\(n\)不是\(3\)的倍数的时候,\(|a_1+a_3-2a_2|=|(a_1-a_2)+(a_3-a_2)|\),不可能让\(a_1-a_2=0\)\(a_3-a_2=0\)同时成立,因此,此时\(0\)并不是一个合法的答案。

考虑构造答案为\(1\)的情况,即我们可以让

  • \(a_1=a_2,a_3=a_1+1,n=a_1+a_2+a_3=3a_1+1\)
  • \(a_1=a_2,a_3=a_1-1,n=a_1+a_2+a_3=3a_1-1\)

上述两种情况涵盖了\(n\)\(3\)求余数为\(1\)\(2\)的情况。

因此,最终的答案就是

  • \(0 : \ if \ (a_1+a_2+a_3)=3k , k\in Z\)
  • \(1 : \ if \ (a_1+a_2+a_3)=3k+1 \ or \ 3k+2 , k\in Z\)

时间复杂度\(O(1)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
signed main()
{
	int t; cin>>t;
	while (t--) {
		int a,b,c; cin>>a>>b>>c;
		if ((a+b+c)%3==0) puts("0");
		else puts("1");
	}
	return 0;
}

Problem B Reverse Sort

长度为\(n(1\leq n \leq 1000)\)的01串,每次操作可以将一个单调不增的子序列翻转。
求最少的操作次数,01串变为单调不降的串。

首先,如果原来的01串已经是单调不降的,那么直接输出0就可以了。
否则,我们可以证明,最多只需要\(1\)步操作,就可以将01串变成单调不降。
构造如下:考虑最终的答案一定是\(cnt_0\)\(0\)后接\(cnt_1\)\(1\)构成的串。
原01串中后\(cnt_1\)个元素中的\(0\)的个数的两倍一定可以与前\(cnt_0\)个元素中的\(1\)一一对应。
因此将这些元素(长度为后\(cnt_1\)个元素中的\(0\)的个数的两倍)构成的子序列翻转,就可以得到目标字符串了。

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
using namespace std;
int main()
{
	int t; cin>>t;
	while (t--) {
		int n; cin>>n;
		string s; cin>>s;
		int cnt=0;
		for (int i=0;i<n;i++) if (s[i]=='1') cnt++;
		vector<int> ans; ans.clear();
		bool flag=false;
		for (int i=n-1;i>=0;i--) if ((s[i]=='0'&&i>=n-cnt)||(s[i]=='1'&&i<n-cnt)) {
			flag=true;
			ans.push_back(i+1);
		} 
		if (flag) {
			printf("1\n%d ",ans.size());
			sort(ans.begin(),ans.end());
			for (int x:ans) printf("%d ",x);
			puts("");
		} else printf("0\n");
	}
	return 0;
}

Problem C Dominant Character

一个长度为\(n(1\leq n \leq 10^6)\)的字符串\(s\),只由\("a","b","c"\)三个字符构成。
询问在这个字符串中,最短的满足下列条件的子串长度是多少:

  • 长度至少为\(2\)
  • 在该子串中,\("a"\)出现的次数严格大于\(b\)出现的次数。
  • 在该子串中,\("a"\)出现的次数严格大于\(c\)出现的次数。

首先,合法的子串两端必须是\(a\),用贪心可以证明其最优性。
其次,一个合法的最短子串,\("a"\)出现的次数\(c_a\),满足\(2 \leq c_a \leq 3\)
换句话说,当一个合法子串\("a"\)出现次数大于\(3\)次,其中必然出现更短的合法子串。
这可以用鸽巢原理解释。

如果观察到上述两个性质,
那么本题只需要记录\(a\)出现的各个位置,记录\(b,c\)出现次数的前缀和,
就可以在\(O(n)\)时间复杂度内求解了。

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N],b[N],c[N];
int main()
{
	int t; cin>>t;
	while (t--) {
		int n; cin>>n;
		for (int i=0;i<=n+1;i++) b[i]=c[i]=0; 
		string s; cin>>s;
		a[0]=0;
		for (int i=0;i<n;i++) {
			if (s[i]=='a') a[++a[0]]=i+1;
			b[i+1]+=b[i]+(s[i]=='b');
			c[i+1]+=c[i]+(s[i]=='c');
		}
		int res = n+1;
		for (int i=1;i<a[0];i++) {
			
			int l=a[i],r=a[i+1];
			
			if (b[r]-b[l-1]<2&&c[r]-c[l-1]<2) res=min(res,r-l+1);
		}
		for (int i=1;i<a[0]-1;i++) {
			int l=a[i],r=a[i+2];
			if (b[r]-b[l-1]<3&&c[r]-c[l-1]<3) res=min(res,r-l+1);
		}
		if (res==n+1) res=-1;
		printf("%d\n",res); 
	}
	return 0;
}

Problem D Treelabeling

A和B在一棵有\(n(1\leq n \leq 2\times 10^5)\)个节点的树上玩游戏,A先手。
先选择一个节点\(u\),将棋子放上去,然后B后手,可以将选择的节点\(v\),当且仅当:

  • \(u,v\)相连且\(v\)没有被棋子放到过。
  • \(u \oplus v \leq min(u,v) , \oplus = xor\)
    先无法行动的一方输。
    可以将节点重新编号,输出一种方案,让A在该编号下第一步可以放棋子的节点数目最多。

考虑\(u \oplus v \leq min(u,v)\)与下描述等价:

  • \(u,v\)的二进制表述下最高位相同。

证明:
如果\(u,v\)最高位相同,显然\(u \oplus v\)\(u\)\(v\)的最高位的对应位为\(0\),小于\(min(u,v)\)
如果\(u,v\)最高位不相同,显然\(u \oplus v\)\(max(u,v)\)的最高位的对应位为\(1\),大于\(min(u,v)\)

考虑在一种特殊的构造下,A无论在何处放棋子,A都立刻必胜,

第一次可以放棋子的节点数位\(n\),显然是最多的。

先对树进行\(01\)染色,那么相邻的节点被染上不同的颜色。

考虑将黑点和白点之间都不能走动,合法的构造是将黑点数目\(b\),和白点数目\(w\)的较小的数字进行二进制分解,然后将对应位置为\(1\)的一系列数都设为黑点或者白点,剩余的点就标上剩余的数字。

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
vector<int>e[N];
int col[N],ans[N];
bool vis[N];
void dfs(int u,int f,int c) {
	col[u]=c;
	for (int v:e[u]) if (v!=f) {
		dfs(v,u,c^1); 	
	}
}
int main()
{
	ios::sync_with_stdio(false);
	int t; cin>>t;
	while (t--) {
		int n; cin>>n;
		for (int i=1;i<=n;i++) {
			e[i].clear();
			ans[i]=0;
			vis[i]=false;
		}
		for (int i=2;i<=n;i++) {
			int u,v; cin>>u>>v;
			e[u].push_back(v);
			e[v].push_back(u);
		}
		dfs(1,0,0);
		int cnt0=0,cnt1=0,p,res;
		for (int i=1;i<=n;i++) if (col[i]==0) cnt0++;
		else cnt1++;
		if (cnt0>cnt1) {
			p=1; res=cnt1;
		} else {
			p=0; res=cnt0;
		}
		vector<int>tmp;
		for (int i=0;i<=20;i++) if ((res>>i)&1) {
			for (int j=1<<i;j<(1<<(i+1));j++)
				tmp.push_back(j);
		}
		int pt=0;
		for (int i=1;i<=n;i++) if (col[i]==p) {
			ans[i]=tmp[pt++];
			vis[ans[i]]=true;
		}
		pt=1;
		for (int i=1;i<=n;i++) if (ans[i]==0) {
			while (vis[pt]&&pt<=n) pt++;
			ans[i]=pt;
			vis[pt]=true;
		}
		for (int i=1;i<=n;i++) printf("%d ",ans[i]);
		puts("");
	}
	return 0;
}

Problem E Array Equalizer

给出长度为\(n(1\leq n \leq 2\times 10^5)\)的两个序列\(a_n,b_n\),\(b_1\)不知道。
每一次操作可以将\(i\)的倍数为下标的元素加\(1\)或者减\(1\),目的是让\(a=b\)
\(q(1\leq q \leq 10^)\)组询问,每组询问表示若\(b_1=x_i(1\leq i \leq q)\)的条件。
每次输出该条件下的最小操作次数。

考虑对于一个确定的\(b_1\),如何计算答案。
\(t_i = b_i - a_i\),通过将\(i\)的倍数为下标的元素\(t_i\)\(1\)或者减\(1\),目的是让\(t_i=0\)
那么每次保证\([1,i]\)是合法的即可。

这可以通过\(O(n+\frac{n}{2}+\frac{n}{3}+...+\frac{n}{n})=O(n log_2 n)\)来计算,

只不过这个时候,带上变量\(x\),只需要将变量系数和常量分别计算即可。

所以这个时候的答案变成一个关于\(x\)的分段函数。

将询问离线排序后,用双指针就可以快速计算出每个询问的答案。

时间复杂度\(O(n log_2 n)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10;
int a[N],b[N],t[N],k[N],ans[N];
map< int,pair<int,int> >mp;
struct QES{
	int v,id;
}qes[N];
struct FUN{
	int l,r; int k,b;
}fun[N];
bool cmp(QES a,QES b) {
	return a.v<b.v;
}
signed main()
{
	ios::sync_with_stdio(false);
	int n; cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=n;i++) cin>>b[i];
	for (int i=2;i<=n;i++) t[i]=b[i]-a[i];
	for (int i=1;i<=n;i++) k[i]=-1;
	for (int i=2;i<=n;i++)
		for (int j=i+i;j<=n;j+=i) {
			t[j]=t[j]-t[i];
			k[j]=k[j]-k[i];
		}
	int q; cin>>q;
	for (int i=1;i<=q;i++) {
		int w; cin>>w; w=w-a[1];
		qes[i].v=w;
		qes[i].id=i;
	}
	sort(qes+1,qes+1+q,cmp);
	int con=0;
	for (int i=1;i<=n;i++) if (k[i]==0) con+=abs(t[i]);
	int K=0,B=con;
	for (int i=1;i<=n;i++) {
		if (k[i]==1) {
			B-=t[i];
			K--;
			mp[-t[i]].first+=2;
			mp[-t[i]].second+=2*t[i];
		} else if (k[i]==-1) {
			B+=t[i];
			K--;
			mp[t[i]].first+=2;
			mp[t[i]].second-=2*t[i];
		}
	}
	int tot=0; 
	++tot;
	fun[tot].l=-1e9; fun[tot].k=K; fun[tot].b=B;	
	for (map< int,pair<int,int> >::iterator it=mp.begin();it!=mp.end();it++) {
		K+=(it->second).first; B+=(it->second).second;
		fun[tot].r=(it->first)-1; ++tot;
		fun[tot].l=(it->first); fun[tot].k=K; fun[tot].b=B;
		if (it==(--mp.end())) break;
	}
	fun[tot].r=1e9;
	int p=1;
	for (int i=1;i<=q;i++) {
		while (qes[i].v<fun[p].l||qes[i].v>fun[p].r) p++;
		ans[qes[i].id]=fun[p].k*qes[i].v+fun[p].b;
	}
	for (int i=1;i<=q;i++) cout<<ans[i]<<endl;
	return 0;
}
posted @ 2021-11-16 11:42  Maystern  阅读(27)  评论(0编辑  收藏  举报