2024 ICPC National Invitational Collegiate Programming Contest, Wuhan Site

题目链接

K. Party Games

思路:

        有一个必胜状态:取完一个数异或和为0.有一个必败状态:一开始异或和为0.其他状态

的异或和为0的状态都可以被提前破坏掉(因为没有两个数一样),所以最终的结束条件一定

是都抽完,则奇数为先手赢。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=1e6+10;
const int mod=1e9+7;
#define fi first
#define se second

int a[N]; 
vector<int> v;
void solve(){
	int n;cin>>n;
	if(a[n-1]==0||(a[n]^a[1])==0) cout<<"Fluttershy"<<endl;
	else{
		if(a[n]==0)
		cout<<"Pinkie Pie"<<endl;
		else if(n&1) cout<<"Fluttershy"<<endl;
		else cout<<"Pinkie Pie"<<endl;
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	v.push_back(1);
	for(int i=1;i<=N-1;i++){
		a[i]=i^a[i-1];
		if(a[i]==0)
			v.push_back(i);
	}
	v.push_back(inf);
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

B. Countless Me

思路:

        n个交换可以实现每个元素的任意变化,问题转化成给一个总数sum将其拆分成n个数

的或和最小。考虑贪心的将最高位置为0,若最高位不能置为0,则尽可能的让所有数的该

最高位都为1。当n个数的除最高位都填满1的时候sum仍然有剩余,就说明该最高位不能为0,

可以考虑从低位加到次高位看总和是否大于sum,从次高位往低位加会爆long long。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
const int mod=1e9+7;
#define fi first
#define se second

ll a[N]; 
void solve(){
	int n;
	cin>>n;
	a[0]=1;
	for(int i=1;i<=62;i++){
		a[i]=a[i-1]*2;
	}
	ll sum=0,ans=0;
	for(int i=1;i<=n;i++){
		ll x;cin>>x;
		sum+=x;
	}
	for(int i=52;i>=0;i--){
		ll p=0;
		for(int j=0;j<i;j++){
			p+=1ll*n*a[j];
			if(p>=sum) break;
		}
		if(p>=sum) continue;
		sum-=a[i]*min((sum/a[i]),1ll*n);
		ans+=a[i];
	}
	cout<<ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

F. Custom-Made Clothes

思路:

答案具有单调性,考虑二分答案。由于矩阵具有单调性,可以用二分或者双指针来找

小于等于mid的数的个数。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=2e5+10;
const int mod=1e9+7;
#define fi first
#define se second

int n,k; 
void solve(){
	cin>>n>>k;
	int l=1,r=n*n;
	auto check=[&](int mid)->bool{
		int sum=0;
		int x=1,y=n;
		while(y>=1&&x<=n){
			cout<<"?"<<' '<<x<<' '<<y<<' '<<mid<<endl;
			int f;cin>>f;
			if(f){
				sum+=y;
				x++;
			}
			else{
				y--;
			}
		}
		return n*n-sum<k;
	};
	while(l<=r){
		int mid=(l+r)/2;
		if(check(mid)) r=mid-1;
		else l=mid+1;
	}
	cout<<"! "<<l;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

E. Boomerang

思路:

        先考虑一个块加上一个点的直径变化:1.仍然为原块的直径,2.直径的一端变为当前添加的点。我们先由根节点深搜一次,找到每个点的深度和父亲,再开个vector来存每个深度的节点。

        若我们由v考虑,在求t的时候谣言树是动态的不好考虑,所以我们将谣言树变成静态的,每次

枚举谣言树每个时间点的状态然后考虑满足该状态的速度。

        枚举时间的时候我们动态加入该时间深度的点先求出直径,然后由于直径的中点不一定存在,所以分类讨论一下半径的值。由于我们固定了谣言树的状态,所以直径是固定的,而又速度

具有单调性,速度小的满足,那么速度大的肯定也满足,所以我们二分速度。

        我们的二分的条件是速度乘时间(即固定时间内的路程)要大于等于直径,若满足该条件,

就意味着该速度及更大的速度都可以在该时间内超过谣言,但显然对于更大的速度超过当前的

谣言时间会更少,并不满足题目所要求的对每一速度求其最小时间,但当我们二分找到边界速度

的时候(好吧,不用二分直接用公式v等于d/t),该速度的最小时间就是当前时间,因为如果时间再少点的话,该速度就超不过现在的谣言了。容易知道时间是块状分布的(也就是对于一个连续的速度可能有同一个时间),然后我们又求出了每个块的左端点,那么只要对每个速度的时间与前面取min即可,因为时间是单调不上升。

代码:

#include<bits/stdc++.h>
/*#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>*/
using namespace std;
const int N=1e6+10;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
typedef unsigned long long ull;
//const ll P=2281701377;
const ll P=998244353;
#define int ll
typedef pair<int,int> pii;

int n;
vector<int> v[N];
int root,t0;
int fa[N][25],dep[N];
vector<int> depp[N];
int depd[N];
int ans[N];
void dfs(int x,int f){
	dep[x]=dep[f]+1;
	depp[dep[x]].push_back(x);
	fa[x][0]=f;
	for(int i=1;i<=20;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(auto y:v[x]){
		if(y==f) continue;
		dfs(y,x);
	}
}
int lca(int a,int b){
	if(dep[a]<dep[b])
		swap(a,b);
	for(int i=20;i>=0;i--){
		if(dep[fa[a][i]]>=dep[b])
			a=fa[a][i];
	}
	if(a==b) return a;
	for(int i=20;i>=0;i--){
		if(fa[a][i]!=fa[b][i]){
			a=fa[a][i],b=fa[b][i];
		}
	}
	return fa[a][0];
}
int get_dis(int a,int b){
	return dep[a]+dep[b]-2*dep[lca(a,b)];
}
void solve(){
	cin>>n;
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		v[a].push_back(b);
		v[b].push_back(a);
	}
	cin>>root>>t0;
	dfs(root,0);
	int d=0;
	int ld=root,rd=root;
	depd[1]=0;
	for(int i=1;i<=t0;i++){
		for(auto j:depp[i+1]){
			if(get_dis(ld,j)>d){
				rd=j,d=get_dis(ld,j);
			}
			if(get_dis(rd,j)>d){
				ld=j,d=get_dis(rd,j);
			}
		}
		depd[i+1]=d;
	}
	memset(ans,0x3f,sizeof ans);
	auto get_d=[&](int s)->int{
		if(s%2) return s/2+1;
		return s/2;
	};
	for(int i=t0+1;i<=t0+n+1;i++){
		for(auto j:depp[i+1]){
			if(get_dis(ld,j)>d){
				rd=j,d=get_dis(ld,j);
			}
			if(get_dis(rd,j)>d){
				ld=j,d=get_dis(rd,j);
			}
		}
		int l=1,r=n;
		int v;
		int dd=get_d(d);
		while(l<=r){
			v=(l+r)>>1;
			if(v*(i-t0)>=dd) r=v-1;
			else l=v+1;
		}
        //if(dd%(i-t0)==0) l=dd/(i-t0);
		//else l=dd/(i-t0)+1;
		if(l<N-10)
		ans[l]=min(ans[l],i);
	}
	for(int i=1;i<=n;i++){
		ans[i]=min(ans[i],ans[i-1]);
		cout<<ans[i]<<' ';
	}
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }

}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值