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;
}