Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)

可惜还是只能 VP。好像这场的 E 重了所以有很多人打差评?

A. Diverse Game

简单的。直接将每个数 \(+1\) 就行了,如果为 \(n\times m\) 就变为 \(1\)

B. Fun Game

一开始读错题了。考虑分别找到 \(s,t\) 中第一个 \(1\) 的位置,若 \(s\) 中的位置大于等于 \(t\) 中的位置那么有解,反之无解。记 \(s\) 中第一个 \(1\) 位置为 \(pos\),考虑每次操作选取区间都形如 \([l,l+pos-1]\),手玩可以发现这个操作可以代替其他所有操作,所以只要 \(pos\) 前面没有要修改成 \(1\) 的地方就行了。

C. Hungry Games

计数题,考虑 dp。设 \(dp_i\) 表示以 \(i\) 为起点的非法子串数,考虑填表法,做一遍前缀和,每次转移时可以二分得到第一个使 \([i,j]\) 区间和大于 \(x\) 的位置 \(j\),从 \(i-1\) 处转移 \(dp_j=dp_j+dp_{i-1}\),如果到了一个 \(i\) 使得 \(sum_{i-1}+x\ge sum_n\) 就 break,答案就是 \(\dfrac{n\times(n+1)}{2}-\sum\limits_{i=0}^{n-1} dp_i\)。注意 dp 初值赋为 \(1\)
这个其实和官解差不多的。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2*114514,M=1919810;
ll T;
ll n,m,k,a[N],sum[N],dp[N]; //以i开头的不合法子串个数? 
string s,t;
void solve(){
	cin>>n>>k;
	dp[0]=1;
	for(int i=1;i<=n;++i) cin>>a[i],sum[i]=sum[i-1]+a[i],dp[i]=1;
	ll pos=1,ans=n*(n+1)/2;
	for(int i=1;i<=n;++i){
		if(sum[i-1]+k>=sum[n]) break;
		ll j=upper_bound(sum+i,sum+n+1,sum[i-1]+k)-sum;
		ans-=dp[i-1],dp[j]+=dp[i-1];
	}
	cout<<ans<<'\n';
}//红温了 
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>T;
	while(T--) solve();
	return 0;
} 

D. Funny Game

明显 \(x\) 越小能连的边就越多,所以可以考虑从大到小枚举 \(x\) 建边。建树肯定要加一个并查集来判断,根据鸽巢原理(或者手玩得到),这 \(x+1\) 个点中一定存在一对能连边的点,那么我们便枚举一个点 \(i\),开数组记录下模 \(x\) 相同且不连通的点,然后并查集维护连通性,最后再倒着输出就可以了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pi pair<ll,ll>
#define fi first
#define se second
const ll N=2*114514,M=1919810;
ll T;
ll n,m,k,a[N],f[N],p[N];
ll find(ll x){
	return x==f[x]?x:f[x]=find(f[x]);
}
vector <pi> out;
void solve(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i],f[i]=i;
	out.clear();
	cout<<"YES\n";
	for(int i=n-1;i>=1;--i){
		ll u=0,v=0;
		for(int j=0;j<=n;++j) p[j]=0;
		for(int j=1;j<=n;++j){
			if(j!=find(j)) continue;
			ll ux=a[find(j)]%i;
			if(p[ux]!=0){
				u=p[ux],v=find(j);
				break; 
			}
			p[ux]=find(j);
		}
		//cout<<u<<" "<<v<<'\n';
		out.push_back({u,v});
		ll x=find(u),y=find(v);
		if(x!=y) f[y]=x;
	}
	for(int i=out.size()-1;i>=0;--i) cout<<out[i].fi<<" "<<out[i].se<<'\n';
}//红温了 
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>T;
	while(T--) solve();
	return 0;
} 

E. Wooden Game

看了题解。我们发现对于一棵树,假设它足够大,只要不断删除叶节点再删除更大的子树就能凑出二进制上的每一位,从而使或的和最大,也就是说答案是和树的形态无关,只和大小有关的。

然后就显然了。从高到低枚举答案在二进制上的每一位,然后枚举哪颗树可以凑出这一位。令当前位数为 \(i\) 对于一棵树,如果凑不出就 continue,否则若答案此为也为 \(0\),那么就让答案与上 1<<i;若答案为 \(1\) 的话就可以让 \(i\) 后面的所有位都与上 \(1\),这样的答案就是最优的。

复杂度 \(O(n\log V)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pi pair<ll,ll>
#define fi first
#define se second
const ll N=1145140,M=1919810;
ll T;
ll n,m,k,x,a[N];
bool cmp(ll x,ll y){
	return x>y;
}
void solve(){
	cin>>k;
	for(int i=1;i<=k;++i){
		cin>>a[i];
		for(int j=1;j<a[i];++j) cin>>x;
	}
	sort(a+1,a+k+1,cmp);
	ll ans=0;
	for(int i=1;i<=k;++i){
		for(int j=24;j>=0;--j){
			ll f1=ans>>j&1,f2=a[i]>>j&1;
			if(f2==0) continue;
			if(f1==0) ans|=1<<j;
			else{
				ans|=(1<<j)-1;
				break;
			}
		}
	}
	cout<<ans<<'\n';
}//红温了 
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>T;
	while(T--) solve();
	return 0;
} 

F. Stardew Valley

没看了

posted @ 2024-07-19 11:36  和蜀玩  阅读(342)  评论(0)    收藏  举报