赛场上只写出来ABC好伤心,其实DEF在看完官方题解时发现也不难

DEF的代码是借鉴官方题解的,指路牛客竞赛

https://ac.nowcoder.com/acm/contest/103948

A.小紫的均势博弈

地上共有n 枚石子,小红和小紫轮流操作,每次拿走一枚石子,谁先不能操作谁就输了。

小红先手操作,请问双方都采用最优策略时,谁会获得最终的胜利?

只需判断奇偶即可。

#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
	cin>>n;
	if(n%2==0)cout<<"yukari";
	else cout<<"kou";
	
}

B.小紫的劣势博弈

题目大意:n个长度的数组,小红小紫轮流操作,小红拿走元素ai,会让x加上ai,小紫则让x减上ai。

小红想让x尽可能小,小紫想让其大。 小红先手,双方都最优条件下,x为多少。

就按照题目意思来就好了,先对数组排序,再依次加减。

#include<bits/stdc++.h>
using namespace std;
int n;
long long a[100005];
long long x;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
	sort(a+1,a+1+n);
    for(int i=1;i<=n;i+=2){
        //if(i+1<=n)cout<<a[i]<<" "<<a[i+1]<<"\n";
        if(i+1<=n)x+=(a[i]-a[i+1]);
    }
    if(n%2!=0)x+=a[n];
    cout<<x;
}

C.小紫的01串操作

博主懒了,不写题目大意了。

这道题目不用考虑复杂,只用考虑什么情况下不能合作成功。 应该就是出现: 000 111 000 1;

我们会发现只要有邻居数字不相同,就肯定要换,那么最坏能合作情况就是邻居不相同次数为4,即:000 111 00

#include<bits/stdc++.h>
using namespace std;
int t;
int main(){
	cin>>t;
    while(t--){
        string s;
        cin>>s;
        int ans=0;
        for(int i=0;i<s.size();i++){
            if(i){
                if(s[i]!=s[i-1])ans++;
            }
        }
        if(ans<=4)cout<<"Yes\n";
        else cout<<"No\n";
    }
	
}

D.小紫的优势博弈

首先先上代码

#include<bits/stdc++.h>
using namespace std;
int n;
string s;
int S[4];
int ans;
int main(){
	cin>>n;
	cin>>s;
	s=' '+s;
	int c1=0,c0=0;//c1表示1的奇偶性,同理c0
	S[0]=1;//此时后缀为空串
	for(int i=n;i>1;i--){
		//if(s[i]=='0')c0^=1;
		//if(s[i]=='1')c1^=1;
		c0^=(s[i]=='0');
		c1^=(s[i]=='1');
		int now=(c0<<1)|c1;//当前状态
		ans+=S[now];
		S[now]=1;
	}
	cout<<1.0*ans/n;
}

当时比赛时没写出来,反思一下是不是没有考虑这题要ac的时间复杂度要在O(n)或者是O(nlogn),因此这说明不会涉及很难的算法。

再根据题目意思,小红先手,小紫后手,且小红随机删除前缀,计算小紫获胜的概率。

随机删除说明我们要依次判断删除不同位置前缀计算

再考虑什么情况下小紫能赢,设删除到的前缀为ai,从i+1开始我只要在能找到一个形成双生串的点就可以了

至于怎么找这个点看下方

怎么找是本题的关键!!!

1.二进制表示状态

2.后缀思想

只要我有两个不同位置的状态一致,就说明在这两个位置之内的连续子串满足题目要求

那么状态有 00 01 10 11

从后往前判断只要遇到1改变1的奇偶性,同理0

再计算出当前状态,查看后缀中有没有同状态的可能,若有ans+1,若无 S[now]=1

E.小紫的线段染色

贪心

其实不难,就是难想到,也是看来官方题解才写出来的,哭哭。

什么时候能自己写出来E和F;

首先你一看这道题会发现题意是好理解的,但是直接考虑怎么样是成功的是有难度的。

正难则反

考虑怎么样会不成功。

用a表示红,b表示紫

aaaaa
    aaaaa
      aaaaa

会发现无论怎么变紫色,总会有两个紫色或者红色相交

只要有一个点被覆盖了三次以上就失败

此时要考虑怎么收集一个点的被覆盖个数!!

排序

判断覆盖点:

	vector<interval>a(n+1);
	vector<pair<int,int>>d;
	for(int i=1;i<=n;i++){
		cin>>a[i].l>>a[i].r;
		a[i].id=i;
		d.emplace_back(a[i].l,1);
		d.emplace_back(a[i].r+1,-1);
	}
	sort(d.begin(),d.end());
	int sum=0;
	for(int i=0;i<d.size();i++){
		sum+=d[i].second;
		if(sum>2){
			cout<<-1<<"\n";
			return;
		}
	}

用到了差分思想

如果一个线段还没结束,另一个线段又开始了,说明一定又重复!!!

完成判断后已经确定是否能成功

然后

先按照左端点排序,在左端点相同的情况下,按照右端点排序(默认从小到大) 即符号重载

struct interval{
	int l,r,id;
	bool operator<(const interval & in) const {
		if(l==in.l){
			return r<in.r;
		}
		return l<in.l;
	}
};

排序完后

按照红紫红紫红紫的顺序记录答案

完整代码

#include<bits/stdc++.h>
using namespace std;
int n;
struct interval{
	int l,r,id;
	bool operator<(const interval & in) const {
		if(l==in.l){
			return r<in.r;
		}
		return l<in.l;
	}
};
void solve(){
	vector<interval>a(n+1);
	vector<pair<int,int>>d;
	for(int i=1;i<=n;i++){
		cin>>a[i].l>>a[i].r;
		a[i].id=i;
		d.emplace_back(a[i].l,1);
		d.emplace_back(a[i].r+1,-1);
	}
	sort(d.begin(),d.end());
	int sum=0;
	for(int i=0;i<d.size();i++){
		sum+=d[i].second;
		if(sum>2){
			cout<<-1<<"\n";
			return;
		}
	}
	sort(a.begin()+1,a.end());
	int r=-1,id=0;
	vector<int>ans(1+n);
	for(int i=1;i<=n;i++){
		if(a[i].l<=r){
			ans[a[i].id]=(ans[id]^1);
		}
		if(a[i].r>r){
			r=a[i].r;
			id=a[i].id;
		}
	}
	vector<int>purple;
	for(int i=1;i<=n;i++){
		if(ans[i])purple.push_back(i);
	}
	if(purple.size()==0){
		purple.push_back(1);
	}
	cout<<purple.size()<<"\n";
	for(int i=0;i<purple.size();i++){
		cout<<purple[i]<<" ";
	}
}
int main(){
	cin>>n;
	solve();
}

F.小紫的树上染色

这题没啥好讲的,就是看到

最大值最小或者是最小值最大

要想到二分答案

#include<bits/stdc++.h>
using namespace std;
int n,k,x,y;
const int N=1e5+10;
vector<int>edge[100005];
vector<int>cnt(N);
int need;
void dfs(int u,int fa,int mid){
	for(auto v :edge[u]){
		if(v==fa)continue;
		dfs(v,u,mid);
		cnt[u]+=cnt[v];
	}
	if(cnt[u]+1>mid){
		need++;
		cnt[u]=0;
	}
	else{
		cnt[u]++;
	}
}
bool check(int mid){
	need=0;
	//cnt.clear();
	cnt.assign(1+n,0);
	dfs(1,0,mid);
	return need<=k;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<n;i++){
		cin>>x>>y;
		edge[x].push_back(y);
		edge[y].push_back(x);
	}
	int l=0,r=n;
	while(l<r){
		int mid=(l+r)/2;
		if(check(mid))r=mid;
		else{
			l=mid+1;
		}
	}
	cout<<l;
	
}

然后清空vector数组要用assign不能用clear