Codeforces Round #749 题解A~F

Codeforces Round #749 题解A~F

回归失败
造个传送门


Problem A Windblume Ode

  • 题意:给一集合,求其最大的元素和非素的子集。
  • 分析:注意到每个元素大小有1<=ai<=200 且元素数量满足3<=n<=100,所以和小于20000。于是我们可以先给集合排序一下(使子集合最大),从小到大生成子集合,最后得到的所有集合中找一下和非素且元素数目最多的就好。

Problem B Omkar and Heavenly Tree

  • 题意:给定m个条件形如a,b,c,要求构造一棵n个节点且满足条件中的b节点不在a、c节点之间的路径上。
  • 分析:注意题目满足m<n,即至少有一个点可以在任意两对节点之间的路径上,所以用这个点构造一个菊花图就好。

Problem C Omkar and Determination

  • 题意:给一矩阵,其中有些点可走,有些点不可走。每次询问x1列至x2列对应子矩阵,有无形如一个点的左边和上边节点都不能走到该子矩阵的最左边一列或最上边一行的情况。
  • 分析:数组lm维护一下当前节点能最左走到的列编号-1(如果能向上走到第一行则置为0),数组mn维护当前列的所有节点中最大的lm。初转移不论当前节点是否可走(因为我们要的只是当前节点是否可确定,考场上被这个坑傻了 ),进行lm[当前节点]=min(lm[左节点],lm[上节点]),每次获得当前l后再更新mn。当然,最后如果当前节点不可走,要将l更新为当前列编号。最后对于每次询问x1、x2,可以用线段树一类的查询一下这个区间上最大的mn值,如果结果大于x1,说明发生了题意所述的情况,即不确定
  • 核心代码:
int main()
{
	n=read();m=read();
	int tn=n*m,p=0;
	for(int i=1;i<=tn;++i) kf[++p]=rech();
	p=0;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			++p;
			if(i==1||j==1){
				lm[p]=0;
			}
			else{
				int a,b;
				a=p-1;b=p-m;
				lm[p]=min(lm[a],lm[b]);
				mn[j]=max(mn[j],lm[p]);
			}
			if(kf[p]) lm[p]=j;
		}
	}
	build(1,1,m);n=read();
	for(int i=1;i<=n;++i){
		fx=read();fy=read();
		int np=query(1,1,m);
		if(np>=fx) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}

Problem D Omkar and the Meaning of Life

  • 题意:交互题。有一排列p,每次可以制造一个序列a满足1<=ai<=n,询问对于序列s(si=pi+ai),最小的满足sk出现次数大于1的k值。如果k不存在,返回0。询问次数要求小于等于2n次。
  • 分析:其实就是排个序,每次令a=1,1,1…1,2,1,…1,1,1,设ai=2,若返回值j<i,则pi+1=pj,称此为有效信息。再令a=2,2,2…2,1,2,…2,2,2,设ai=2,若返回值j<i,则pi=pj+1,称此为有效信息。有效信息个数可以发现一定为n-1+2(n-1个顺序,2次返回0),所以根据+1顺序就可以推出p咯。
  • 核心代码:
int main()
{
	int n=read();fflush(stdout);
	for(int i=n;i>=1;--i){
		for(int j=1;j<=n;++j) q[j]=2;
		q[i]=1;
		printf("? ");fflush(stdout);
		for(int j=1;j<=n;++j){
			printf("%d ",q[j]);fflush(stdout);
		}
		printf("\n");fflush(stdout);
		int pt=read();fflush(stdout);
		if(pt<i) r[pt]=i;
	}
	for(int i=n;i>=1;--i){
		for(int j=1;j<=n;++j) q[j]=1;
		q[i]=2;
		printf("? ");fflush(stdout);
		for(int j=1;j<=n;++j){
			printf("%d ",q[j]);fflush(stdout);
		}
		printf("\n");fflush(stdout);
		int pt=read();fflush(stdout);
		if(pt<i) r[i]=pt;
	}
	int pt=r[0],cnt=0;
	while(pt){
		p[pt]=++cnt;pt=r[pt];
	}
	printf("! ");fflush(stdout);
	for(int i=1;i<=n;++i){
		printf("%d ",p[i]);fflush(stdout);
	}
	return 0;
}

Problem E Moment of Bloom

看官方题解才会的/(ㄒoㄒ)/

  • 题意:给一个n点m边的图(原图保证联通且无重边自环),及q次操作(a,b),每次可以选择a->b的一条路径,对路径上每条边边权加1(初始边权均为0)。问经过所有操作后,是否可能所有边的边权均为偶数。若可以,则输出每次操作的路径;否则,输出至少还需要增加多少次操作,才能使所有边权均为偶数。
  • 分析:
  1. 对于每次操作,我们可以发现只会对a和b的一条邻边边权加1,所以对于q次操作后,某一节点a的邻边边权增量为f[a],f[a]表示节点a在询问中出现的次数,显然存在f[a]为奇数,则不可以使所有边为偶数。
  2. 另外,对于a,b路径上的点c,其有两条邻边的边权各加1,前面已经提到f[a]及f[b]为奇数不合法,所以只考虑f[a]和f[b]均为偶数的情况,这样点c就经过了两次,两条邻边边权各加2,不存在不合法情况。所以不合法情况只需要考虑是否有节点a满足f[a]为奇数。
  3. 对于合法时输出路径,只需要将整张图看作一棵树(不影响分析1,2),每次dfs即可。至于不合法,只需要将每个奇数f[a]增1即可,数量就是这样节点的数量的一半咯。
  • 代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int mx=300005;
int n,m,cnt,nq;
bool flag;
int num[mx],zu[mx],f[mx];
struct node{
	int y,next;
}rd[mx<<1];
struct quer{
	int a,b;
}q[mx];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
	return x*f;
}
int father(int x)
{
	if(x!=zu[x]) zu[x]=father(zu[x]);
	return zu[x];
}
inline void add(int u,int v)
{
	rd[++cnt].y=v;rd[cnt].next=num[u];num[u]=cnt;
}
void dfs(int u,int ed,int las)
{
	if(u==ed){
		printf("%d\n",cnt+1);
		for(int i=1;i<=cnt;++i) printf("%d ",zu[i]);
		printf("%d\n",ed);flag=true;
		return;
	}
	zu[++cnt]=u;int v;
	for(int i=num[u];i;i=rd[i].next){
		v=rd[i].y;if(v==las) continue;
		dfs(v,ed,u);if(flag) return;
	}
	--cnt;
}
int main()
{
	n=read();m=read();int u,v;
	for(int i=1;i<=n;++i) zu[i]=i;
	for(int i=1;i<=m;++i){
		u=read();v=read();
		if(father(u)!=father(v)){
			zu[zu[v]]=u;add(u,v);add(v,u);
		}
	}
	nq=read();
	for(int i=1;i<=nq;++i){
		q[i].a=read();q[i].b=read();
		++f[q[i].a];++f[q[i].b];
	}
	cnt=0;
	for(int i=1;i<=n;++i) if(f[i]%2) ++cnt;
	if(cnt){
		printf("NO\n%d",cnt>>1);
	}
	else{
		printf("YES\n");
		for(int i=1;i<=nq;++i){
			cnt=0;flag=false;
			dfs(q[i].a,q[i].b,0);
		}
	}
	return 0;
}

Problem F Defender of Childhood Dreams

  • 题意:给序列长度n和限定k,序列1~n满足对任意a<b,则a到b有边,求染色方案,使染色后任意长度大于等于k的路径上颜色种类数大于1,同时保证使用颜色种类最少。
  • 分析:(感觉比E好想
  1. 先考虑对于长度为k的序列怎么染色呢,当然染一样的就好啦。那么对于k2长度的序列呢?先依次k个k个的染色(每次染色一个节点数为k的序列),染色后视为一个节点,不就是一个新的长度为k的序列咯,再染一次就可以了,所以要染2次。以此类推,长度为n的序列就要染logkn次上取整咯。
  2. 上面就是最佳染色方案咯。如果减少任意一种颜色,比如说最后一种颜色,换作之前的某种颜色,就一定会出现长度为k的单色路径哟。
  • 代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mx=1005,maxn=1000005;
int n,k,cnt,ans;
struct node{
	int u,v,w;
	bool operator<(const node &p)
	const{
		if(p.u==u) return v<p.v;
		return u<p.u;
	}
}rd[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
	return x*f;
}
int main()
{
	n=read();k=read();
	int len=1;
	while(len<n){
		++ans;len*=k;
		for(int i=1;i<=n;++i){
			int p=(i-1)/len;p=p*len+len;
			int bg=(i-1)/(len/k);bg=bg*(len/k)+(len/k);
			for(int j=bg+1;j<=p&&j<=n;++j){
				rd[++cnt].u=i;rd[cnt].v=j;rd[cnt].w=ans;
	//			printf("i=%d j=%d\n",i,j);
			}
		}
	}
	printf("%d\n",ans);
	sort(rd+1,rd+1+cnt);
	for(int i=1;i<=cnt;++i){
//		printf("\n(%d,%d)---",rd[i].u,rd[i].v);
		printf("%d ",rd[i].w);
	}
	return 0;
}

Conclusion:

CF题的思路雀实有趣 ACM新赛季rp++

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hiroxzwang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值