zoukankan      html  css  js  c++  java
  • 博弈论专项练习

    CF1458E

    先不考虑特殊点,考虑把两堆石子的状态看成坐标轴上的点,那么每一行每一列只有一个非法状态,考虑把所有非法状态处理出来,用意发现这是一条 (45) 度的斜线。考虑它碰到了一个特殊点,假设这个特殊点在他左边,也就是说它从下面撞上了这个特殊点引出来的线平行于 x 轴的那一条,这相当于对其做了次向上偏移。我们考虑维护这个折线,有一种很好的写法是把截距相同的一段直接记在map里。具体实现可以参考代码(我也是参考别人的)

    #include <bits/stdc++.h>
    using namespace std;
    const int inf=1e9;
    const int MAXN=100005;
    template <typename T>
    void read(T &x) {
    	T flag=1;
    	char ch=getchar();
    	for (; '0'>ch||ch>'9'; ch=getchar()) if (ch=='-') flag=-1;
    	for (x=0; '0'<=ch&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
    	x*=flag;
    }
    struct node{
    	int x, y;
    }p[MAXN];
    int n, m;
    map<int, int> mnx, mny;
    map<pair<int, int>, bool> mark;
    map<int, map<int, int> > mp;
    int main() {
    	read(n); read(m);
    	for (int i=1, x, y; i<=n; i++) {
    		read(x); read(y);
    		mark[make_pair(x, y)]=true;
    		if (mnx.find(x)==mnx.end()) mnx[x]=y;
    		else mnx[x]=min(mnx[x], y);
    		if (mny.find(y)==mny.end()) mny[y]=x;
    		else mny[y]=min(mny[y], x);
    	}
    	int nowx=0, nowy=0;
    	for (; nowx<=inf&&nowy<=inf; ) {
    		if (mnx.find(nowx)!=mnx.end()&&mnx[nowx]<=nowy) {
    			nowx++;
    			continue;
    		}
    		if (mny.find(nowy)!=mny.end()&&mny[nowy]<=nowx) {
    			nowy++;
    			continue;
    		}
    		if (mnx.find(nowx)!=mnx.end()||mny.find(nowy)!=mny.end()) {
    			mark[make_pair(nowx, nowy)]=true;
    			nowx++;
    			nowy++;
    			continue;
    		}
    		map<int, int> :: iterator x=mnx.lower_bound(nowx), y=mny.lower_bound(nowy);
    		int tmp=inf;
    		if (x!=mnx.end()) tmp=min(tmp, (*x).first-nowx);
    		if (y!=mny.end()) tmp=min(tmp, (*y).first-nowy);
    		mark[make_pair(nowx, nowy)]=true;
    		mp[nowx-nowy][nowx+tmp]=nowx;
    		nowx+=tmp;
    		nowy+=tmp;
    	}
    	for (int i=1; i<=m; i++) {
    		int a, b;
    		read(a); read(b);
    		if (mark[make_pair(a, b)]) puts("LOSE");
    		else {
    			map<int, int> :: iterator it=mp[a-b].upper_bound(a);
    			if (it!=mp[a-b].end()&&(*it).second<=a) {
    				puts("LOSE");
    			} else {
    				puts("WIN");
    			}
    		}
    	}
    	return 0;
    }//kel
    

    CF1147F

    这道题有一个关键是第一步不需要考虑前面的限制,那么我们猜想先手(B)不知道怎么输。考虑任意取出一组最大匹配中的两条匹配边 ((u,v),(p,q)),不妨设先手选择的是 Increasing,那么对于 (w(v,p)>w(u,v))(w(p,q)<w(v,p)) 那么这组匹配被称作好的。对于一组好的匹配,我们发现后手只要走匹配边即可。下面证明一定存在好的匹配,我们只需找出这组好的匹配即可。如果 (exists w(u,v)<w(v,p)<w(p,q)),我们将这组匹配改为 ((p,v),(u,q)) 即可变成一组好的,容易发现这就是我们在做稳定婚姻问题是更改匹配边的过程,所以我们对左侧点定义满意度为从小到大,右侧点为从大到小跑稳定婚姻问题即可。

    #include <bits/stdc++.h>
    #define pii std::pair<int,int>
    #define mp std::make_pair
    #define F first
    #define S second
    const int maxn=105;
    template<typename T>
    void read(T &x){
    	T flag=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')flag=-1;
    	for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    	x*=flag;
    }
    int n,a[maxn][maxn],rnk[maxn][maxn],pos[maxn],nxt[maxn],p;
    std::priority_queue<pii>pq;
    std::queue<int>q;
    void solve(){
    	read(n);
    	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)read(a[i][j]);
    	putchar('B');putchar('
    ');
    	fflush(stdout);
    	char ch;scanf("%c",&ch);
    	if(ch=='D')for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=-a[i][j];
    	read(p);
    	if(p>n)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=-a[i][j];
    	for(int i=1;i<=n;i++){
    		q.push(i);
    		for(int j=1;j<=n;j++)pq.push(mp(-a[i][j],j+n));
    		int tmp=0;
    		while(!pq.empty()){
    			int u=pq.top().S;pq.pop();
    			rnk[i][++tmp]=u;
    		}
    	}
    	for(int i=1;i<=n+n;i++)nxt[i]=0;
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		if(!u)continue;
    		for(int i=1;i<=n&&!nxt[u];i++){
    			int w=rnk[u][i];
    			if(!nxt[w]||a[u][w-n]>a[nxt[w]][w-n])nxt[nxt[w]]=0,q.push(nxt[w]),nxt[u]=w,nxt[w]=u;
    		}
    	}
    	while(1){
    		if(p==-1||p==-2)break;
    		printf("%d
    ",nxt[p]);fflush(stdout);
    		read(p);
    	}
    }
    int main(){
    	int T;scanf("%d",&T);
    	while(T--)solve();
    	return 0;
    }
    

    CF1033

    这道题得发现一个看起来很套路但又不太敢相信它是对的的套路。设当前局面为 (S),定义 (S') 为每个 (v' o v mod (a+b)) 后的局面,则 (S)(S') 的赢家是同样的人。

    证明咕。

    然后考虑计数,当 A 或 B 必胜时只与 ((a,b)) 有关,所以令 (a'=b,b'=a) 那么胜负关系就反转了,所以 A 胜的方案一定与 B 胜的方案相同,所以我们只需要求出先手胜和后手胜的方案。注意每个 (v'_i<a+b)

    #include<bits/stdc++.h>
    #define pb push_back
    #define mp std::make_pair
    #define pii std::pair<int,int>
    #define F first
    #define S second
    typedef long long ll;
    typedef unsigned long long ull; 
    namespace _name{
    	const int maxn=105,mod=998244353,inf=0x3f3f3f3f;
    	template<typename T>
    	inline void read(T &x){
    		T flag=1;
    		char ch=getchar();
    		for(;!isdigit(ch);ch=getchar())if(ch=='-')flag=-1;
    		for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    		x*=flag;
    	}
    	template<typename T>
    	inline void write(T x){
    		if(x==0)return putchar('0'),void();
    		int stk[20],top=0;
    		while(x)stk[++top]=x%10,x/=10;
    		for(int i=top;i;i--)putchar(stk[i]+'0');
    	}
    	template<typename T>
    	inline void print(T x,char End='
    '){
    		if(x<0)x=-x,putchar('-');
    		write(x);putchar(End);
    	}
    	inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
    	inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
    	inline int mul(int a,int b){return 1ll*a*b%mod;}
    	inline int ksm(int a,int b=mod-2){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
    }using namespace _name;
    int n;
    ll m,v[maxn],w[maxn],ans[2];
    int main(){
    	read(n);read(m);for(int i=1;i<=n;i++)read(v[i]);
    	for(int s=2;s<=2*m;s++){
    		for(int i=1;i<=n;i++)w[i]=v[i]%s;
    		std::sort(w+1,w+1+n,[](const int a,const int b){return a>b;});
    		w[0]=s-1;
    		for(int i=0,id=0;i<=n;i++,id^=1){
    			int l=std::max(w[i+1],w[id+1]/2)+1,r=std::min(m,w[i]);
    			ans[id]+=std::max(0,std::min(r,s-l)-std::max(l,s-r)+1);
    		}
    	}
    	print((1ll*m*m-ans[0]-ans[1])/2,' ');print((1ll*m*m-ans[0]-ans[1])/2,' ');print(ans[1],' ');print(ans[0]);
    	return 0;
    }
    

    AGC010D

    如果存在一个 (1) 那么只用看其他数的和的奇偶性。否则如果没有偶数那么后手只要模仿先手即可,先手必败。所以如果只有一个偶数那么先手必胜。如果有奇数个偶数,注意到不可能全是偶数(因为一开始的 (gcd)(1)),那么先手可以保证一直都是奇数个偶数,最后一定会变成 (1) 个偶数,先手必胜。考虑偶数的偶数,因为先手想赢,他只有一种办法,那就是只有一个奇数,然后给这个奇数-1,剩下全除以 (gcd),这种情况直接递归去做即可。

    #include <bits/stdc++.h>
    using namespace std;
    int n;
    int a[100005], cnt;
    long long sum;
    int ans;
    void work(int id) {
    	sum=0;
    	cnt=0; 
    	for (int i=1; i<=n; i++) {
    		if (a[i]==1) {
    			for (int j=1; j<=n; j++) sum+=a[j]-1;
    			if(sum&1) ans=id;
    			else ans=1-id;
    			return;
    		}
    		if(!(a[i]&1)) cnt++;
    	}
    	if (cnt&1) ans=id;
    	else {
    		if (n-cnt>1) ans=1-id;
    		else {
    			int g=0;
    			for (int i=1; i<=n; i++) if (a[i]&1) a[i]--;
    			for (int i=1; i<=n; i++) g=__gcd(g, a[i]);
    			for (int i=1; i<=n; i++) a[i]/=g;
    			work(1-id);
    		}
    	}
    	
    }
    int main() {
    	scanf("%d", &n);
    	for (int i=1; i<=n; i++) scanf("%d", a+i);
    	work(1);
    	if (ans) puts("First");
    	else puts("Second");
    	return 0;
    }
    

    AGC026F

    对于 (n) 为偶数的情况,通过反证法可以得知一定选左右开始。
    对于奇数情况,由上可知一定选一个偶数的位置,然后后手会得到主动权然后递归去做,最后先手选了奇数的一段
    考虑先全部选偶数,然后把一段改为奇数,这可以通过前缀和 (+s[r]-s[l-1]) 实现。
    直接二分答案,然后求出合法区间,要求这些合法区间必须首位相连,即 (r_{i}+1=l_{i+1}-1)
    我们可以使用一个 dp 判断,设 (dp_{i}) 代表 [1,i] 有没有合法区间划分,我们只需找到一个 (dp_{j}=1)(s[i-1]-s[j]>=mid)
    然后发现可以优化直接取最小的 (s[j]) 即可,复杂度 (O(nlog{n}))

    #include <bits/stdc++.h>
    #define pii std::pair<int,int>
    #define mp std::make_pair
    #define F first
    #define S second
    const int maxn=300005;
    template<typename T>
    void read(T &x){
    	T flag=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')flag=-1;
    	for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    	x*=flag;
    }
    int n,w[maxn],s[maxn],zz[2];
    bool check(int m){
    	int mn=0;//dp[0]
    	for(int i=2;i<=n;i+=2)if(s[i-1]-mn>=m)mn=std::min(mn,s[i]);
    	return s[n]-mn>=m;
    }
    int main(){
    	read(n);for(int i=1;i<=n;i++)read(w[i]),zz[i&1]+=w[i];
    	if(!(n&1)){
    		printf("%d %d
    ",std::max(zz[0],zz[1]),std::min(zz[0],zz[1]));
    	}else{
    		for(int i=1;i<=n;i++)s[i]=s[i-1]+(i&1?w[i]:-w[i]);
    		int l=0,r=zz[0]+zz[1],res=0;
    		while(l<=r){
    			int mid=(l+r)>>1;
    			if(check(mid))l=mid+1,res=mid;
    			else r=mid-1;
    		}
    		printf("%d %d
    ",zz[0]+res,zz[1]-res);
    	}
    	return 0;
    }
    
  • 相关阅读:
    python数据结构之字典
    Python数据结构之列表
    使用QrCode生成二维码
    JavaScript在不同环境下的全局对象
    记一次使用 removeEventListener 移除事件监听失败的经历
    Model、View、ViewModel结构以及全局视图模型注入器的说明
    MVVMLight介绍以及在项目中的使用
    WPF/Silverlight深度解决方案:(一)解锁被Storyboard束缚的关联属性
    进程发送消息
    .net4.0中wpf单例启动
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/15371905.html
Copyright © 2011-2022 走看看