【无标题】

\2022河南萌新联赛第(一)场:河南工业大学\Alice_and_Bob

//题意:Alice 和 Bob 正在玩一个游戏,双方都很聪明。游戏是这样的,给出一个正整数 n,然后每次轮流操作,

//每次操作需要将数 n 除以  a^k。Alice 先手,谁先将数 n变为 1 则谁输。(a必须为质数,且a^k能够整除n,a,k为正整数)

//anti Nim

//有n堆石子(n>0),每一堆有ai个石子每人每次可以从任意一堆石子里,取出任意多枚石子扔掉,

//可以取完,不能不取,每次只能从一堆里取。最后没有石子可以取的人输掉这场游戏。

//思路:将n分解成质因数,每个质因数为一堆石子的一个,转化成anti nim博弈

//先手必胜的条件为

// 所有堆的石子数均 = 1 ,且有偶数堆,这个偶数堆在这个情况下其实就是异或和为0 。

// 至少有一个堆的石子数 > 1,且石子堆的异或和 ≠ 0。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using namespace std;



#define endl '\n'

typedef pair pr;



#define int long long

#define ll long long

#define fr(i,l,r) for(int i=l;i<=r;i++)

#define ufr(i,n,z) for(int i = n;i >= z; i--)

#define pb(x) push_back(x)

#define all(a) a.begin(),a.end()

#define fi first

#define se second



const int N = 1e6+10;

const int mod=998244353,inf=LONG_LONG_MAX;

int n,m;



int a[N];



void solve()

{

   int n;

   cin>>n;

   int ans=0;

   bool flag=0;

   for(int i=2;i*i<=n;i++){             //质因数分解

      if(n%i==0){

        int cnt=0;

        while(n%i==0){

            cnt++;

            n/=i;

        }

        if(cnt>1){

            flag=1;

        }

        ans^=cnt;

      }

   }

   if(n>1){

      ans^=1;

   }

   if((!flag&&!ans)||(flag&&ans)){

      cout<<"Alice win"<<'\n';

   }

   else{

      cout<<"Bob win"<<'\n';

   }

}

signed main()

{

    int t=1;

 //   cin>>t;

    while(t--) solve();

    return 0;

}

sg函数
 1.可选步数为1~m的连续数sg(x)=x%(m+1)
 2.可选步数为任意个数sg(x)=x
 3.可选不连续个数:打表
f[]:可以取走的石子个数
 

sg[]:0~n的SG函数值
hash[]:mex{}
int f[N], sg[N], hash[N];
void getSG(int n)
{
    int i, j;
    memset(sg, 0, sizeof(sg));
    for (i = 1; i <= n; i++){
        memset(hash, 0, sizeof(hash));
        for (j = 1; f[j] <= i; j++)
            hash[sg[i - f[j]]] = 1;
        for (j = 0; j <= n; j++){    //求mes{}中未出现的最小的非负整数
            if (hash[j] == 0){
                sg[i] = j;
                break;
            }
        }
    }
}

经典博弈总结

anti Nim
有n堆石子(n>0),每一堆有ai个石子每人每次可以从任意一堆石子里,取出任意多枚石子扔掉,
可以取完,不能不取,每次只能从一堆里取。最后没有石子可以取的人输掉这场游戏。
先手必胜的条件为
 所有堆的石子数均 = 1 ,且有偶数堆,这个偶数堆在这个情况下其实就是异或和为0 。
 至少有一个堆的石子数 > 1,且石子堆的异或和 ≠ 0。
 
斐波那契博弈(Fibonacci Nim)
有n张纸牌,A,B两人轮流按照以下规则取牌。
A先取,但是不能在第一次将纸牌全部取完,而且至少要取一张;
每次所取纸牌张数必须大于或等于1, 且小于等于对手刚取的纸牌张数的两倍。取到最后一张牌者为胜者。
结论:⑴如果牌的张数n是Fibonacci数时,先取牌者必败。
⑵对所有非Fibonacci数都是先取人必赢,反之,必败。

stairacse nim
有n堆石头,每次可以从一堆中拿出一些或全部石头给相邻的右边的一堆石头,
或者最后一堆减去一些或全部石头,谁不能操作谁输,
假设从最后一堆石头开始与上一堆相间的石头数的异或和为P,P为0时先手必败反之必胜。
比如a1,a2,a3,a4,a5   P的值就是a5 ^ a3  ^ a1

wythoff game
有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,
多者不限,最后取光者得胜。
公式
 

#include
using namespace std;
int a, b;
int main() {
    while (cin >> a >> b) {
        int sum = a + b;
        a = min(a, b);
        b = sum - a;
        if ((int)(double(b - a) * (sqrt(5) + 1) / 2) == a) cout <<"先手输" << endl;
        else cout << "先手胜" << endl;
    }
    return 0;
}

bash博弈
只有一堆n个物品, 两个人轮流从这堆物品中取物, 规定每次至少取一个, 最多取m个.
最后取光着胜
n%(m + 1) = 0,先手输。反之先手必胜。
最后取光者输
当n = (m + 1) × k + 1时,先手输,否则先手必胜。

二维数点

步骤总结

1.将查询分为四个,2.离散化,3.按x轴排序,4遇见操作操作,遇见查询查询

5.将查询的以二维前缀和的方式算出
解决:给出一个二维平面內的若干个点,多次询问某个矩形区域內包含多少个点(边界也算)。
又或者,给一个长为n的序列,多次询问[l.r]内有多少值在[x,y]的元素的个数
P2163 [SHOI2007] 园丁的烦恼
给定n个坐标,q次询问,每次询问给定一个矩形,求在矩形里的点

#include 
#include
#include
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define rep(i,a,b) for(int i=(a);i<=(b);i++)

typedef long long ll;
typedef pair  pii;
inline int IntRead() { char ch = getchar(); int s = 0, w = 1; while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = getchar(); }while (ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }return s * w; }
#define y1 y114514
#define MAX 2000000 + 50
int n, m, k, op;
int x1, x2, y1, y2;
struct ran {
    int x, y;
}tr[MAX];
bool cmp(ran a, ran b) {
    if (a.x != b.x)return a.x < b.x;
    else return a.y < b.y;
}
int tot;
struct ranran {
    int x, y, id;
}ar[MAX];
bool cmpp(ranran a, ranran b) {
    if (a.x != b.x)return a.x < b.x;
    else return a.y < b.y;
}
int sum[MAX];
inline int lowbit(int x) {
    return x & (-x);
}
inline int getans(int x) {
    int ans = 0;
    for(int i=x;i;i-=i&-i){
        ans += sum[i];
    }
    return ans;
}
inline void insert(int x, int c) {
    for (int i = x; i <= n + 2 * m;i+=i&-i) {
        sum[i] += c;
    }
}
int ans[MAX];
int main() {
    n = IntRead(); m = IntRead();
    vectorv;
    for (int i = 1; i <= n; ++i) {
        tr[i].x = IntRead(); tr[i].y = IntRead();
        v.push_back(tr[i].y);
    }
    for (int i = 1; i <= m; ++i) {
        x1 = IntRead(); y1 = IntRead();
        x2 = IntRead(); y2 = IntRead();
        v.push_back(y2); v.push_back(y1 - 1);
        ++tot; ar[tot].id = tot; ar[tot].x = x2; ar[tot].y = y2;
        ++tot; ar[tot].id = tot; ar[tot].x = x2; ar[tot].y = y1 - 1;
        ++tot; ar[tot].id = tot; ar[tot].x = x1 - 1; ar[tot].y = y2;
        ++tot; ar[tot].id = tot; ar[tot].x = x1 - 1; ar[tot].y = y1 - 1;
    }
    sort(tr + 1, tr + 1 + n, cmp);             //按x
    sort(ar + 1, ar + 1 + tot, cmpp);          
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    int cnt = 1;
    for (int i = 1; i <= tot; ++i) {
        while (cnt <= n && tr[cnt].x <= ar[i].x) {   //先操作
            int p = (int)(lower_bound(v.begin(), v.end(), tr[cnt].y) - v.begin());
            insert(p + 1, 1);
            ++cnt;
        }
        int p = (int)(lower_bound(v.begin(), v.end(), ar[i].y) - v.begin());  //查询
        ans[ar[i].id] += getans(p + 1);
    }
    for (int i = 1; i <= tot; i += 4) {
        printf("%d\n", ans[i] + ans[i + 3] - ans[i + 1] - ans[i + 2]);
    }
    return 0;
}

你可能感兴趣的:(算法)