2024 ICPC National Invitational Collegiate Programming Contest, Wuhan Site基础题K I B F E

Problem - I - Codeforces

贪心

光看题看不懂让干嘛,但看了题目数据的注释就明白了题目大意就是一个01串,让你通过最少的操作使得串的前一部分全是0后一部分全是1,操作指选择一个子串将这个子串某个位置后的所有字符移到子串的前面。

我们将所有这样11...1100...00的串当成一个需要操作的移动一次,计算移动次数。因为如果这个串前面有字符且字符是1,则和这个串融为一个,如果是0则满足0在1前,这次操作后这个子串及前面所有字符仍然满足0在1前。

#include<bits/stdc++.h>
using namespace std;
string s;
int n,a[100001],t,ans;
int main()
{
	cin>>s;
	n=s.length();
	for(int i=0;i<n;i++)
	a[i+1]=s[i]-'0';
	for(int i=1;i<=n;i++)
	{
		if(a[i]) t=1;
		if(!a[i])
		{
			if(t) ans++;
			t=0;
		}
	}
	printf("%d",ans);
}

Problem - K - Codeforces

博弈论

1 2 3 ..... n 的串,如果异或和不为0就可以从开头或结尾去掉一个数,否则败,一人执行一次操作,问当n值确定先手胜还是后手胜。

通过列举一些二进制数可以发现一个规律:0~3,4~7,8~11.....,以4为周期的二进制数的异或和为0,此时n可以分解成三个部分,0001  0010  0011 ....... xx00 xx01 xx10 xx11 

当n%4==0时,序列为0001  0010  0011 ....... xx00,先手去掉xx00后序列的异或和为0,后手败。

当n%4==1时,序列为0001  0010  0011 ....... xx00 xx01,先手不会去xx01,否则后手参照情况一先手败。先手只能去0001,此时xx和xx异或和为0,xx01恰好顶替了0001,序列的异或和为0。

当n%4==2时,序列为0001  0010  0011 ....... xx00 xx01 xx10,先手如果去xx10,则回到情况二,先手败,去0001,后手去xx10,此时0010 0011 xx00 xx10恰好异或和为0

当n%4==3时,序列为0001  0010  0011 ....... xx00 xx01 xx10 xx11,此时异或和已经为0,先手败。

#include<bits/stdc++.h>
using namespace std;
int T,n;
void work()
{
	scanf("%d",&n);
	if(n%4<=1) printf("Fluttershy\n");
	else printf("Pinkie Pie\n");
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{work();}
}

Problem - B - Codeforces

贪心

将一个数列变为和不变的另外一个数列,问新数列的或|和最小是多少。

采用贪心的思想,从高位看,如果这个位上可以不填1,就不填,必须要填,就尽可能多的填。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,a[200001],s,t,ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]),s+=a[i];
	//if(s%n==0)printf("%d",s/n);
	for(int i=30;i>=0;i--)
	{
		t=(1<<i);
		if(s/t>=n)
		{
			s-=n*t;
			ans+=t;
		}
		else
		{
			if((t-1)*n<s)
			{
				s-=(s/t*t);
				ans+=t;
			}
		}
	}
	printf("%d",ans);
}

Problem - F - Codeforces

这道题考试时看了很长时间都看不懂到底在干嘛,之后才知道了这种题叫交互题。一般都是让你去输出东西询问,系统输入“YES”或“NO”作为回答,然后让你根据这些“YES”和“NO”得到最终答案。这道题用二分写,具体不说了(主要不会)

Problem - E - Codeforces

动态树的直径

通过分析题意发现答案具有单调性且树的结构是动态变化的,每一秒钟向外扩展一次,这里可以考虑bfs来进行扩展操作,根据队列性质发现每当从队列中取出新一层的点,这时队列中只含有这一层的所有点,扩展操作进行完一次。此时如果速度*(t-t0)*2>=此时树的直径则说明此时这个速度可行,因为具有单调性,可通过二分找到可行的最小速度。最后扩展完还没结束,因为较小的速度不一定能在扩展结束之前可行,我们还需直接计算剩下的速度还需多长时间走完直径。

直径的计算方法:设当前直径端点为u,v,新加入的点为y,此时如果存在新的直径只有可能为(u,y)或(v,y)。只需计算uv,vy距离这里套用距离计算公式:dis(u,y)=d(u)+d(y)-2*d(lca(u,y))

#include <bits/stdc++.h>
using namespace std;
const int N=200001;
queue<int> q,q1;
int n,rr,t0,d[N],zj,u,w,l,r,ans=n+1,anss[N],v[N],vis[N],t,f[N][20];
int ver[N*2],Next[N*2],head[N],tot;
void add(int x,int y)
{
	ver[++tot]=y;
	Next[tot]=head[x],head[x]=tot;
}
void ycl()
{
	q1.push(rr);
	while(q1.size())
	{
		int x=q1.front();q1.pop();
		for(int i=head[x];i;i=Next[i])
		{
			int y=ver[i];
			if(d[y]||y==rr) continue;
			d[y]=d[x]+1;
			f[y][0]=x;
			for(int j=1;j<=t;j++)
			f[y][j]=f[f[y][j-1]][j-1];
			q1.push(y);
		}
	}
}
int lca(int x,int y)
{
	if(d[x]>d[y]) swap(x,y);
	for(int i=t;i>=0;i--)
	if(d[f[y][i]]>=d[x]&&f[y][i]) 
	y=f[y][i];
	if(x==y) return x;
	for(int i=t;i>=0;i--)
	if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
void ef(int t)
{
	int p=ans-1;
	l=1,r=p;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(mid*(t-t0)*2>=zj)
		{
			r=mid-1;
			ans=mid;
		}
		else l=mid+1;
    }
	for(int i=ans;i<=p;i++)
	anss[i]=t;
}
void bfs()
{
	while(q.size())
	{
		int x=q.front();q.pop();
		if(d[x]>t0&&!vis[d[x]])
		{
			vis[d[x]]=1;
			ef(d[x]);
			if(ans==1) return ;
		}
		for(int i=head[x];i;i=Next[i])
		{
			int y=ver[i];
			if(v[y]) continue;
			q.push(y);v[y]=1;
			int uu=lca(y,u),ww=lca(y,w);
			if(d[y]+d[u]-2*d[uu]>=d[y]+d[w]-2*d[ww]&&d[y]+d[u]-2*d[uu]>zj)
			zj=d[y]+d[u]-2*d[uu],w=y;
			else if(d[y]+d[w]-2*d[ww]>d[y]+d[u]-2*d[uu]&&d[y]+d[w]-2*d[ww]>zj)
			zj=d[y]+d[w]-2*d[ww],u=y;
		}
	}
}
void continu()
{
	for(int i=ans-1;i>=1;i--)
	{
		if(zj%(2*i)==0) anss[i]=zj/(2*i)+t0;
		else anss[i]=zj/(2*i)+1+t0;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	scanf("%d%d",&rr,&t0);
	t=log(n)/log(2)+1;
	ycl();
	q.push(rr);
	v[rr]=1;
	ans=n+1;
	u=rr,w=rr;
	bfs();
	if(ans!=1) continu();
	for(int i=1;i<=n;i++)
	printf("%d ",anss[i]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值