AtCoder Grand Contest 014

A - Cookie Exchanges

若都为偶数且相同,显然无限轮。
否则考虑存在不同的最低位,每经过一轮之后会 − 1 -1 1,所以就只有 l o g log log次。

#include<bits/stdc++.h>
using namespace std;

int a,b,c;

int dfs(int x,int y,int z){
	if((x&1) || (y&1) || (z&1)) return 0;
	return dfs((y+z)/2,(x+y)/2,(x+z)/2)+1;
}

int main(){
	scanf("%d %d %d",&a,&b,&c);
	if(a==b && b==c && a%2==0) printf("-1\n");
	else printf("%d\n",dfs(a,b,c));
}

B - Unplanned Queries

建出询问的生成树,观察对于每一个联通块是否都满足即可。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct edge{
	int y,nex;
}s[N<<1];
int first[N],len=0,n,m,fa[N];
bool tf[N],vis[N],we=true;

int findpa(int x){return fa[x]!=x?fa[x]=findpa(fa[x]):fa[x];}

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

void dfs(int x,int fa){
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa)
		dfs(s[i].y,x),tf[x]^=tf[s[i].y];
	we&=(tf[x]^1);
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) fa[i]=i;
	int x,y;
	for(int i=1;i<=m;i++){
		scanf("%d %d",&x,&y);
		int fx=findpa(x),fy=findpa(y);
		if(fx!=fy){
			fa[fx]=fy;
			ins(x,y);
		}
		tf[x]^=1,tf[y]^=1; 
	}
	for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
	printf(we?"YES\n":"NO\n");
}

C - Closed Rooms

显然移动一次之后,剩下的 k k k步都是走满的, b f s bfs bfs枚举第一次走到的位置即可。

#include<bits/stdc++.h>
using namespace std;

const int N=810;
int n,m,k,ans=1e9,st,ed;
int fx[4]={-1,1,0,0};
int fy[4]={0,0,-1,1},vis[N][N];
pair<int,int> qs[N*N];
char s[N][N];

int calc(int x,int y){
	return min(min((x-1+k-1)/k,(n-x+k-1)/k),min((y-1+k-1)/k,(m-y+k-1)/k));
}

void bfs(int x,int y){
	qs[st=ed=1]=make_pair(x,y);
	memset(vis,-1,sizeof(vis));vis[x][y]=k;
	while(st<=ed){
		pair<int,int> X=qs[st++];
		ans=min(ans,calc(X.first,X.second)+1);
		if(!vis[X.first][X.second]) continue;
		for(int i=0;i<4;i++){
			int xx=X.first+fx[i],yy=X.second+fy[i];
			if(xx<1 || xx>n || yy<1 || yy>m || s[xx][yy]=='#' || vis[xx][yy]!=-1) continue;
			vis[xx][yy]=vis[X.first][X.second]-1;
			qs[++ed]=make_pair(xx,yy);
		}
	}
}

int main(){
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=n;i++)
		scanf("%s",s[i]+1);
	int x,y;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) if(s[i][j]=='S')
			bfs(i,j);
	printf("%d\n",ans);
}

D - Black and White Tree

先手必定操作一个叶子的邻接点,这样可以控制后手操作这个叶子,否则后手会影响更多的点,不优。
两次操作之后,直接将这两个点及其到周围点的连边删除,剩下就是个子问题。
容易发现,奇数图先手必胜。
偶数图的话,若一个点有两个或以上的奇数子树,那么先手必胜。其他情况后手必胜。
两个奇数子树,可以拿一个奇数子树出来与根节点相接,形成一个偶数子树,操作完之后就剩下一个单独的奇数联通块,操作它就先手必胜。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct edge{
	int y,nex;
}s[N<<1];
int first[N],len=0,n,sz[N];
bool tf=false;

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

void dfs(int x,int fa){
	sz[x]=1;
	int tot=0;
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
		dfs(s[i].y,x),sz[x]+=sz[s[i].y];
		if(sz[s[i].y]&1) tot++;
	}
	if((n-sz[x])&1) tot++;
	if(tot>=2) tf=true;
}

int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<n;i++){
		scanf("%d %d",&x,&y);
		ins(x,y),ins(y,x);
	}
	dfs(1,0);
	printf(tf?"First\n":"Second\n");
}

E - Blue and Red Tree

树链剖分板子题。
考虑一次操作之后分为两个互不相关的联通块。
c i , d i c_i,d_i ci,di路径加 1 1 1,之后每次找出一条仅被覆盖一次的边,并将覆盖它的删除即可。
这样操作肯定没有问题,否则无解。
具体的话线段树上使用 v e c t o r vector vector来维护,维护区间最小值,支持区间 + 1 , − 1 +1,-1 +1,1,删除的时候将 log ⁡ \log log个点的 v e c t o r vector vector清空就可以保证时间复杂度,时间复杂度正确 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct edge{
	int y,nex;
}s[N<<1];
int n,first[N],len=0,fa[N],sz[N],son[N],top[N],dfn[N],tim,dep[N];
bool tf[N];
pair<int,int> p[N];
int mmin[300010],tag[300010];
vector<int> V[300010];

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

void dfs_1(int x){
	sz[x]=1;
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa[x]){
		fa[s[i].y]=x;dep[s[i].y]=dep[x]+1;
		dfs_1(s[i].y);
		sz[x]+=sz[s[i].y];
		if(sz[s[i].y]>sz[son[x]]) son[x]=s[i].y;
	}
}

void dfs_2(int x,int tp){
	dfn[x]=++tim;top[x]=tp;
	if(son[x]) dfs_2(son[x],tp);
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa[x] && s[i].y!=son[x])
		dfs_2(s[i].y,s[i].y);
}

#define ls now<<1
#define rs now<<1|1
void psd(int now){
	if(tag[now]){
		tag[ls]+=tag[now];
		tag[rs]+=tag[now];
		mmin[ls]+=tag[now];
		mmin[rs]+=tag[now];
		tag[now]=0;
	}
}
int X,num;

void adt(int now,int x,int y,int l=2,int r=n){
	if(x==l && y==r){
		V[now].push_back(X);
		tag[now]++;mmin[now]++;
		return ;
	}
	psd(now);
	int mid=(l+r)/2;
	if(y<=mid) adt(ls,x,y,l,mid);
	else if(mid<x) adt(rs,x,y,mid+1,r);
	else adt(ls,x,mid,l,mid),adt(rs,mid+1,y,mid+1,r);
	mmin[now]=min(mmin[ls],mmin[rs]);
}

void dt(int now,int x,int y,int l=2,int r=n){
	if(x==l && y==r){tag[now]--;mmin[now]--;return ;}
	psd(now);
	int mid=(l+r)/2;
	if(y<=mid) dt(ls,x,y,l,mid);
	else if(mid<x) dt(rs,x,y,mid+1,r);
	else dt(ls,x,mid,l,mid),dt(rs,mid+1,y,mid+1,r);
	mmin[now]=min(mmin[ls],mmin[rs]);
}

void gs(int now,int l=2,int r=n){
	if(mmin[now]!=1) {num=-1;return ;}
	for(int i=0;i<V[now].size();i++) if(!tf[V[now][i]]){
		num=V[now][i];
		break;
	}
	V[now].resize(0);
	if(l==r) {mmin[now]=1e9;return ;}
	int mid=(l+r)/2;
	psd(now);
	if(mmin[ls]==1) gs(ls,l,mid);
	else gs(rs,mid+1,r);
	mmin[now]=min(mmin[ls],mmin[rs]);
}

void solve(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[y]]>=dep[top[x]]) swap(x,y);
		adt(1,dfn[top[x]],dfn[x]);x=fa[top[x]];
	}
	if(dep[x]>=dep[y]) swap(x,y);
	if(x!=y) adt(1,dfn[x]+1,dfn[y]);
}

void dsolve(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[y]]>=dep[top[x]]) swap(x,y);
		dt(1,dfn[top[x]],dfn[x]);x=fa[top[x]];
	}
	if(dep[x]>=dep[y]) swap(x,y);
	if(x!=y) dt(1,dfn[x]+1,dfn[y]);
}

int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<n;i++){
		scanf("%d %d",&x,&y);
		ins(x,y),ins(y,x);
	}
	dfs_1(1);dfs_2(1,1);
	for(int i=1;i<n;i++){
		scanf("%d %d",&p[i].first,&p[i].second);
		X=i;solve(p[i].first,p[i].second);
	}
	for(int i=1;i<n;i++){
		gs(1);
		if(num==-1){printf("NO\n");return 0;}
		tf[num]=1;
		dsolve(p[num].first,p[num].second);
	}
	printf("YES\n");
}

F - Strange Sorting

前面的题好简单,最后一题好难。
首先考虑增量法,假设已经处理出值为 [ 2 , n ] [2,n] [2,n]的答案,现在要求值为 [ 1 , n ] [1,n] [1,n]的答案。
a n s [ i + 1 ] = 0 ans[i+1]=0 ans[i+1]=0 ,若 1 1 1在最前面,那么 a n s [ i ] = 0 ans[i]=0 ans[i]=0,否则 a n s [ i ] = 1 ans[i]=1 ans[i]=1
否则,我们就来玩一玩。
首先证明一个小引理: 2 2 2 a n s [ i + 1 ] − 1 ans[i+1]-1 ans[i+1]1次操作后一定不在 [ 2 , n ] [2,n] [2,n]中的第一个。
考虑反证法即可,若 2 2 2在第一个那么必然是 high \text{high} high,而必定存在 low \text{low} low点, 2 2 2 1 1 1此操作后必定不在首位,矛盾。
假设在第一位的这个元素为 x x x
再证明一个小引理: x x x high \text{high} high当且仅当 x x x [ 1 , n ] [1,n] [1,n]中的第一个位置。
考虑反证法即可,如果 x x x high \text{high} high且不在第一个位置。
前面就存在另一个 high \text{high} high,一次操作之后, x x x会排在这个 high \text{high} high的后面。
如果两者还是相同类型的的, x x x会一直跟在他后面。
如果 x x x变成 high \text{high} high了,就有新的一个前面的值换着跟。
在有限次的操作之后, x x x永远都到不了第一个,矛盾。
可以发现,在 a n s [ i + 1 ] − 1 ans[i+1]-1 ans[i+1]1次操作后,如果 x , 1 , 2 x,1,2 x,1,2三者的排列是 ( x , 1 , 2 ) (x,1,2) (x,1,2),那么 a n s [ i ] = a n s [ i + 1 ] ans[i]=ans[i+1] ans[i]=ans[i+1],否则 a n s [ i ] = a n s [ i + 1 ] + 1 ans[i]=ans[i+1]+1 ans[i]=ans[i+1]+1
再来证明一个定理: x , 1 , 2 x,1,2 x,1,2三者的循环关系一次排序后中永远不变。
循环关系指的是 ( 1 , 2 , 3 ) = ( 2 , 3 , 1 ) = ( 3 , 1 , 2 ) (1,2,3)=(2,3,1)=(3,1,2) (1,2,3)=(2,3,1)=(3,1,2)
证明可以这样考虑:
首先如果 x x x不在首尾必为 low \text{low} low
若为 ( x , 1 , 2 ) (x,1,2) (x,1,2) ( x , 2 , 1 ) (x,2,1) (x,2,1)
x x x low \text{low} low,那么 1 , 2 1,2 1,2也为 low \text{low} low,不变。
x x x high \text{high} high,那么 1 , 2 1,2 1,2 low \text{low} low,转变为 ( 1 , 2 , x ) (1,2,x) (1,2,x) ( 2 , 1 , x ) (2,1,x) (2,1,x),循环关系不变。
若为 ( 2 , x , 1 ) (2,x,1) (2,x,1) ( 2 , 1 , x ) (2,1,x) (2,1,x)
2 2 2 low \text{low} low,那么 1 , x 1,x 1,x也为 low \text{low} low
2 2 2 high \text{high} high,那么 1 , x 1,x 1,x low \text{low} low,转变为 ( x , 1 , 2 ) (x,1,2) (x,1,2) ( 1 , x , 2 ) (1,x,2) (1,x,2),循环关系不变。
若为 ( 1 , x , 2 ) (1,x,2) (1,x,2)
1 1 1 low \text{low} low,那么 2 , x 2,x 2,x low \text{low} low
1 1 1 high \text{high} high 2 , x 2,x 2,x low \text{low} low,转变为 ( x , 2 , 1 ) (x,2,1) (x,2,1),循环关系不变。
若为 ( 1 , 2 , x ) (1,2,x) (1,2,x)
1 1 1 low \text{low} low,那么 2 , x 2,x 2,x low \text{low} low
1 , 2 1,2 1,2 high \text{high} high x x x low \text{low} low,转变为 ( x , 1 , 2 ) (x,1,2) (x,1,2),循环关系不变。
1 1 1 high \text{high} high 2 2 2 low \text{low} low x x x low \text{low} low,转变为 ( x , 1 , 2 ) (x,1,2) (x,1,2),循环关系不变。
最后记录一下 a n s ans ans和每一轮的 x x x D p Dp Dp即可。

#include<bits/stdc++.h>
using namespace std;

const int N=200010;
int n,a[N],pos[N],p[N],ans[N];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),pos[a[i]]=i;
	for(int i=n-1;i>=1;i--){
		ans[i]=ans[i+1];
		if(ans[i]==0){
			if(pos[i]>pos[i+1]) 
				ans[i]++,p[i]=i+1;
		}
		else {
			if(pos[p[i+1]]<pos[i] && pos[i]<pos[i+1] ||
			   pos[i]<pos[i+1] && pos[i+1]<pos[p[i+1]] ||
			   pos[i+1]<pos[p[i+1]] && pos[p[i+1]]<pos[i]) p[i]=p[i+1];
			else{
				ans[i]++;
				p[i]=i+1;
			}
		}
	}
	printf("%d\n",ans[1]);
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值