CF1406 游记

鄙人打的第一场 cf ,还好遇到了一场比较简单的。

A Subset Mex

题意:给定可重集合 \(S\) ,将其分为两个集合 \(A\)\(B\) ,使得 \(\operatorname{mex}(A)+\operatorname{mex}(B)\) 最大。

挺简单的一道签到题,贪心地想,如果一个数出现了大于两次,那么肯定两个集合各分配一个,如果只出现了一次,直接往一个集合怼就可以了。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
int cnt[105];
int main(){
	int T;read(T);
	while(T--){
		memset(cnt,0,sizeof cnt);
		int n;read(n);
		for(int i=1;i<=n;++i){
			int a;read(a);++cnt[a];
		}
		int o=0;
		while(cnt[o]>=2)++o;
		int oo=o;
		while(cnt[oo]>=1)++oo;
		write(o+oo),pc('\n');
	}
	return 0;
}

B Maximum Product

题意:给定 \(n\) 个数,选出 \(5\) 个数使得其乘起来最大。

考虑到:

\[a<b\Leftrightarrow\begin{cases}a\cdot c<b\cdot c & c>0 \\ a\cdot c>b\cdot c & c<0 \end{cases} \]

直接 dp 记录最大值最小值即可。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
long long mx[6],mn[6];
int main(){
	int T;read(T);
	while(T--){
		memset(mx,-0x3f,sizeof mx);
		memset(mn,0x3f,sizeof mn);
		mx[0]=mn[0]=1;int n;read(n);
		for(int i=1;i<=n;++i){
			long long a;read(a);
			for(int j=min(i,5);j>=1;--j){
				mx[j]=max(mx[j],max(mx[j-1]*a,mn[j-1]*a));
				mn[j]=min(mn[j],min(mx[j-1]*a,mn[j-1]*a));
			}
		}
		write(mx[5]),pc('\n');
	}
	return 0;
}

题意等价于:给定一棵 \(n\) 个点的树,请断掉一条边再加上一条边,使得得到的还是一棵树,同时重心唯一。

如果重心本来就唯一,删掉一条边再连回去就可以了,如果重心不唯一,由于 \(n\ge 3\) ,重心肯定不是叶子节点,把一个重心的某条边(不是连接两个重心的那条边)连出去的子树接到另一棵子树上就行了,正确性显然。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=100005;
struct Edge{
	int v,nt;
	Edge(int v=0,int nt=0):
		v(v),nt(nt){}
}e[maxn*2];
int hd[maxn],num;
void qwq(int u,int v){
	e[++num]=Edge(v,hd[u]),hd[u]=num;
}
int sz[maxn];
void getsize(int u,int fa){
	sz[u]=1;
	for(int i=hd[u];i;i=e[i].nt){
		int v=e[i].v;
		if(v==fa)continue;
		getsize(v,u);sz[u]+=sz[v];
	}
}
int r0,r1,n;
void findroot(int u,int fa){
	for(int i=hd[u];i;i=e[i].nt){
		int v=e[i].v;
		if(v==fa||sz[v]*2<n)continue;
		if(sz[v]*2>n)return findroot(v,u);
		r0=u,r1=v;return;
	}
	r0=u;return;
}
int main(){
	int T;read(T);
	while(T--){
		memset(hd,0,sizeof hd);num=0;
		read(n);
		for(int i=1;i<n;++i){
			int u,v;
			read(u),read(v);
			qwq(u,v);qwq(v,u);
		}
		getsize(1,0);
		r0=r1=0;
		findroot(1,0);
		if(!r1){
			int x=1,y=e[hd[1]].v;
			write(x),pc(' '),write(y),pc('\n');
			write(x),pc(' '),write(y),pc('\n');
		}
		else{
			int qaq=0;
			for(int i=hd[r0];i;i=e[i].nt){
				int v=e[i].v;
				if(v!=r1){
					qaq=v;
					break;
				}
			}
			write(r0),pc(' '),write(qaq),pc('\n');
			write(r1),pc(' '),write(qaq),pc('\n');
		}
	}
	return 0;
}

D Three Sequences

题意:给定长度为 \(n\) 数组 \(a\) ,构造两个长度为 \(n\) 的数组 \(b\)\(c\) ,使得 \(\forall 1\le i\le n,a_i=b_i+c_i\) ,并且 \(b\) 单调不升, \(c\) 单调不降,并且 \(\max(b_i,c_i)\) 最小,有 \(q\) 次操作,每次操作都会给出 \(l,r,x\) ,令 \(\forall l\le i\le r,a_i\gets a_i+x\) ,对于每次操作,求出这次操作完后最小的 \(\max(b_i,c_i)\)

\(a\) 数组进行差分, \(b\) 相当于是除了第一个值以外后面的数都非正数, \(c\) 相当于是后面的都非负数,相当于是要最小化 \(\max(\sum_{i=1}^nb_i,c_1)\) ,对于任意的 \(i\ge 2\) ,如果 \(a_i\le 0\) ,肯定全部分配给 \(c_i\) ,否则全部分配给 \(b_i\) ,这样才能最小化 \(\sum_{i=2}^nb_i\) ,不妨设 \(s=\sum_{i=2}^nb_i\) ,那么相当于是要最小化 \(\max(b_1+s,c_1)\) ,其中 \(b_1+c_1=a_1\) ,当 \(b_1+s=c_1\) 时最小,但是注意到必须取整数,所以答案就是 \(\lceil\frac{a_1+s}{2}\rceil\) ,修改就是维护 \(s\) ,这个很简单。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=100005;
long long a[maxn];
long long ans(long long a,long long b){
	return b+(((a-b)&1)?(a-b+1)/2:(a-b)/2);
}
int main(){
	int n;read(n);
	for(int i=1;i<=n;++i)
		read(a[i]);
	long long sum=0;
	for(int i=n;i>=2;--i){
		a[i]-=a[i-1];
		if(a[i]>=0)sum+=a[i];
	}
	write(ans(a[1],sum));pc('\n');
	int q;read(q);
	while(q--){
		int l,r;long long x;
		read(l),read(r),read(x);++r;
		if(l==1)a[l]+=x;
		else{
			if(a[l]>=0)sum-=a[l];
			a[l]+=x;
			if(a[l]>=0)sum+=a[l];
		}
		if(r<=n){
			if(a[r]>=0)sum-=a[r];
			a[r]-=x;
			if(a[r]>=0)sum+=a[r];
		}
		write(ans(a[1],sum)),pc('\n');
	}
	return 0;
}

总结

E 我不会写,可以去看看 Imakf 的博客

第一场 cf ,感觉题目难度对新手不错,在 B 上面卡了一下,其他题目感觉还是很好的。

一开始以为自己只能写出 AB ,结果一直切完了 D ,到 22:30 的时候,刚好把 E 题目看完,我已经没有时间再做了,于是就只好离场了( E 反正不会做)。

体验不错,题目蛮有意思的,不知道下次还能不再打,希望时间能都在 9:30 左右开始就好了。

原文地址:https://www.cnblogs.com/lsq147/p/13661861.html