20201005国庆day3

博弈论:

1.公平组合游戏:
  若一个游戏满足:
        a.两名玩家交替行动。
        b.在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关。
        c.不能行动的玩家判负。
  则为一个公平组合游戏。
2.状态图:
  在公平组合游戏中可以用有向无环图表示状态,p态和n态。p状态表示该状态对于前一个玩家必胜,n状态表示该状态对于后一个玩家必胜。
  大部分游戏中,终止状态是p态。任意一个p态,它要么是一个终止状态,要么它所有可以转移到的状态都是n态。而对于任意一个n态,至少有一个后继状态是p态。
3.Nim游戏

n堆石子,第i堆有\(a_i\)个,两玩家轮流挑选一堆石子,取走若干个,最后不能取的是输家。
对于当前状态 \(a=(a_1,a_2...a_n)\),如果 \(a_1\hat{}a_2\hat{}...\hat{}a_n=0\),则为p态,否则为n态。
结论:Nim博弈先手必胜,当且仅当 \(a_1\hat{}a_2\hat{}...\hat{}a_n\neq0\)

T1(nim游戏和经典取石子游戏的结合)

对于x的答案,我们将每个数都mod(x+1),并求出异或和。假如异或和为0,则假如Alice取某堆石子的数量超过了余数,Bob可以取同一堆的石子使得余数变回去。如果Alice取的不超过某堆石子的余数,那么游戏变为经典的nim游戏,所以异或和为0先手必败,否则必胜。
所以问题变为求所有可能的x对应的 \(a_imod(x+1)\)的异或和。

50pts。。。

T2

30pts思路:维护三个堆,分别为x能吃的鱼,x不能吃的鱼,已经吃了的鱼。来回倒腾就行。
24pts代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
	priority_queue<int>q;
	priority_queue<int,vector<int>,greater<int> >l;
	priority_queue<int>eat;
	int n,a;scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a);q.push(a);
	}
	scanf("%lld",&a);
	int chk;
	while(a--){
		scanf("%lld",&chk);
		int x,y;
		if(chk==2){
			scanf("%lld",&x);
			q.push(x);
		}
		else if(chk==3){
			scanf("%lld",&x);
			while(q.top()>x){
				l.push(q.top());
				q.pop();
			}
			q.pop();
			while(!l.empty()){
				q.push(l.top());
				l.pop();
			}
		}
		else{
			int cnt=0;
			scanf("%lld%lld",&x,&y);
			while(q.top()>=x){
				l.push(q.top());
				q.pop();
			}
			while(!q.empty()){
				if(x>=y)break;
				x+=q.top();
				cnt++;
				eat.push(q.top());
				q.pop();
				while(l.top()<x&&!l.empty()){
					q.push(l.top());
					l.pop();
				}
			}
			if(x>=y)printf("%lld\n",cnt);
			else printf("-1\n");
			while(!l.empty()){
				q.push(l.top());l.pop();
			}
			while(!eat.empty()){
				q.push(eat.top());eat.pop();
			}
		}
	}
	return 0;
}

正解:线段树二分。

T3

40pts:\(n^2\)暴力显然
100pts:计算范围内倍数数量即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
int gcd(int a,int b){
    if (a%b==0) return b;
    gcd(b,a%b);
}
ll a,b,c,d;
int main(){
    scanf ("%lld%lld%lld%lld",&a,&b,&c,&d);
    ll ans=0;
    for (int i = 1;i <= 999;i++){
        for (int j = 1;j <= 999;j++){
            if (gcd(i,j)==1){
                ll l=max((a-1)/i+1,(c-1)/j+1),r=min(b/i,d/j);
                ans=(ans+max((ll)(0),r-l+1)*(i+j))%mod;
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-10-05 15:44  zdxx  阅读(108)  评论(0编辑  收藏  举报