2016多校第一场

1002 chess  

tag:博弈论 SG函数应用 状态压缩并记录

题目的意思是给你n行20列的棋盘, 每一行上面会有k个棋子, 告诉你棋子的位置, 现在两个人开始轮流玩游戏,规则如下:

1:一个人可以选一个棋子移动到这个棋子右边的空位上

2:如果右边没有空位那么跳过右边的棋子移动到最近的空位上

分析:我们可以模拟这个玩游戏的过程打出SG值即可。代码如下:

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>

using namespace std;
int n;
int dp[(1<<20)+100];

int dfs(int st) {
    if(dp[st] != -1) return dp[st];
    bool ex[50] = {0};
    for(int i=0; i<20; i++) {
        if(((st>>i)&1) == 0) continue;
        int j = i+1;
        while(j<20 && ((st>>j)&1)==1) j++;
        if(j < 20) {
            int tp = st;
            tp ^= (1<<i), tp ^= (1<<j);
            int s = dfs(tp);
            ex[s] = 1;
        }
    }
    int sg = 0;
    while(ex[sg]) sg++;
    dp[st] = sg;
    return sg;
}

int main() {
    int T;
    memset(dp, -1, sizeof(dp));
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        int SG = 0;
        while(n--) {
            int num; scanf("%d", &num);
            int st = 0;
            for(int i=0; i<num; i++) {
                int t; scanf("%d", &t);
                st |= (1<<(t-1));
            }
            SG ^= dfs(st);
        }
        if(SG) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}



1004 gcd

题意:题目意思是给你一个长度为N(100000)的序列,然后会有m个查询, 询问l-r区间的gcd glr是多少, 以及整个序列中gcd等于glr的个数。

分析:首先我们可以用ST表来预处理出区间的gcd, 然后我们注意到以一个端点为开始的gcd是以2的倍数递减的, 因此以l为开始的gcd最多有logN个不同的gcd, 这样我们枚举每个端点, 将所有的gcd预处理出来存储到map里面即可。处理的时候使用二分查找来找两个端点。代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <map>

using namespace std;
const int maxn = 100000 + 100;
int N, M;
int a[maxn];
map<int, long long> mp;

int ST[maxn][20];
int gcd(int m, int n) {
    if(n == 0) return m;
    else return gcd(n, m%n);
}
void init() {
    for(int i=1; i<=N; i++)
        ST[i][0] = a[i];
    for(int j=1; (1<<j)<=N; j++)
        for(int i=1; i<=N; i++) {
            ST[i][j] = ST[i][j-1];
            if(i+(1<<(j-1)) > N) continue;
            ST[i][j] = gcd(ST[i][j-1], ST[i+(1<<(j-1))][j-1]);
        }
}
int query(int l, int r) {
    int k = log((double)r-l+1)/log(2.0);
    return gcd(ST[l][k], ST[r-(1<<k)+1][k]);
}

void getnum(int L, int g, int &r1, int &r2) {
    int l = L, r = N;
    while(l <= r) {
        int mid = (l+r)/2;
        int tp = query(L, mid);
        if(tp == g) r1 = mid, r = mid-1;
        else if(tp > g) l = mid + 1;
        else r = mid - 1;
    }
    l = L, r = N;
    while(l <= r) {
        int mid = (l+r)/2;
        int tp = query(L, mid);
        if(tp == g) r2 = mid, l = mid + 1;
        else if(tp > g) l = mid + 1;
        else r = mid - 1;
    }
}

void init2() {
    mp.clear();
    for(int l=1; l<=N; l++) {
        int g = a[l];
        while(1) {
            int r1, r2;
            getnum(l, g, r1, r2);
            mp[g] += r2-r1+1;
            if(r2 == N) break;
            g = query(l, r2+1);
        }
    }
}

int main() {
    int T;
    scanf("%d", &T);
    int kase = 0;
    while(T--) {
        scanf("%d", &N);
        for(int i=1; i<=N; i++) scanf("%d", &a[i]);
        init();
        init2();
        scanf("%d", &M);
        printf("Case #%d:\n", ++kase);
        for(int i=1; i<=M; i++) {
            int l, r; scanf("%d%d", &l, &r);
            printf("%d %lld\n", query(l, r), mp[query(l, r)]);
        }
    }
    return 0;
}


//1
//5
//1 2 4 6 7
//4
//1 5
//2 4
//3 4
//4 4
//
//Case #1:
//1 8
//2 4
//2 4
//6 1


1005 necklace

tag:二分图匹配

题意:你有2*N个珠子,其中有N个白珠子 N个黑珠子, 现在想把这些珠子串成项链(首尾相接),相邻的两个珠子不能相同, 规定有M个限制条件a, b, 表示黑珠子a和白珠子b相邻的时候黑色的珠子会变色, 问你在最优的情况下最少有几个黑珠子变色?

分析:这个问题可以稍稍的做一下转化, 即对于某个特定顺序的白珠子,每一个黑色的珠子可以插到某些白珠子中间, 问你最少有几个黑珠子不能匹配?这下就将问题转化成了匹配问题,我们只需枚举白色珠子的圆排列顺序然后求最大匹配更新答案即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int maxn = 100;
int V;    //顶点数
vector<int> G[maxn];
int match[maxn];
bool used[maxn];
void add_edge(int u, int v) {
    //printf("u = %d, v = %d\n", u, v);
    G[u].push_back(v);
    G[v].push_back(u);
}

bool dfs(int v) {
    used[v] = true;
    for(int i=0; i<G[v].size(); i++) {
        int u = G[v][i], w = match[u];
        if(w<0 || (!used[w] && dfs(w))) {
            match[v]  = u;
            match[u] = v;
            return true;
        }
    }
    return false;
}

int bipartite_matching() {
    int res = 0;
    memset(match, -1, sizeof(match));
    for(int v = 0; v<V; v++) {
        if(match[v] < 0) {
            memset(used, 0, sizeof(used));
            if(dfs(v)) res++;
        }
    }
    return res;
}

int N, M;  //N个珠子 M个限制条件
int a[maxn];
int conflict[maxn][maxn];

int main() {
    while(~scanf("%d%d", &N, &M)) {    // 0 - N-1
        memset(conflict, 0, sizeof(conflict));
        for(int i=0; i<M; i++) {
            int a, b; scanf("%d%d", &a, &b);
            conflict[a-1][b-1] = 1;
        }
        for(int i=0; i<N; i++) a[i] = i;
        int ans = 0x3f3f3f3f;
        do {
//            for(int i=0; i<N; i++) printf("%d ", a[i]);
//            printf("\n");
            for(int i=0; i<=2*N; i++) G[i].clear();
            for(int ya=0; ya<N; ya++) {   //阳开始匹配
                for(int yi=0; yi<N; yi++) {
                    int pre = (yi-1+N)%N, nxt = yi;
                    if(!conflict[ya][a[pre]] && !conflict[ya][a[nxt]]) add_edge(ya, N+yi);
                }
            }
            V = 2*N;
            int res = bipartite_matching();
            //printf("res = %d\n", res);
            ans = min(ans, N-res);
            if(ans == 0) break;
        }while(next_permutation(a+1, a+N));
        printf("%d\n", ans);
    }
    return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值