Live2D

2021-07-06 集训题解

完美串

题目传送门

Description

Solution

可以(不能)发现的是,对于一个长度为 \(n\)\(01\) 串,\(1\) 的个数为 \(i\) 时的合法 \(01\) 串在旋转意义下本质相同,然后你只需要构造一个然后判断就好了。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 1055

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}

#define IT vector<int>::iterator

int n;
char s[MAXN];

void makeit (int ned0,int ned1,vector <int> &S){	
	if (ned0 == ned1){
		for (Int i = ned0 + ned1 - 1;~i;-- i) S.push_back (i & 1);
		return ;
	}
	else if (ned0 > ned1){
		makeit (ned0 - ned1,ned1,S);
		for (IT it = S.begin();it != S.end();)
			if (*it) it = S.insert (++ it,0);
			else ++ it;
		return ;
	}
	else{
		makeit (ned0,ned1 - ned0,S);
		for (IT it = S.begin();it != S.end();)
			if (!*it) it = S.insert (++ it,1);
			else ++ it;
		return ;
	}
}

bitset <MAXN> S1,S2;

int getit (vector <int>&S){
	bitset <MAXN> cur,rev;int id = 0;
	for (IT it = S.begin();it != S.end();cur[id ++] = *it ++);
	rev = cur;int tmp = 0;
	do{
		tmp += (S2 & (S1 ^ cur)).none();
		cur[n] = cur[0],cur >>= 1;
	}while (cur != rev);
	return tmp;
}

signed main(){
    freopen ("A.in","r",stdin);
    freopen ("A.out","w",stdout);
    int cnt0 = 0,cnt1 = 0;
    read (n),scanf ("%s",s);
    for (Int i = 0;i < n;++ i) if (s[i] != '?') S1[i] = (s[i] == '1'),S2[i] = 1,cnt0 += (s[i] == '0'),cnt1 += (s[i] == '1');
    if (cnt0 == n || cnt1 == n) return puts ("1") & 0;
    else{
    	int ans = (cnt1 == 0) + (cnt0 == 0);
    	for (Int i = max(1,cnt1);i <= min (n - 1,n - cnt0);++ i){
    		vector <int> S;
    		makeit (n - i,i,S);
    		ans += getit (S);
    	}
    	write (ans),putchar ('\n');
    }
 	return 0;
}

行列式

题目传送门

Description

Solution

不难想到,假设矩阵为 \(A\),你可以展开成 \(\det(A)=\det(B+C)\)\(B\) 里面全是 \(x\),这个就相当于构造新矩阵,每一行从 \(B,C\) 中选一行的矩阵行列式之和。那么可以想到的是,\(B\) 最多只能选一行,否则交换两行就可以抵消掉了。

然后你发现 \(p_i<i\),暗示了构成了一棵树,然后你发现真的是一棵树,如果不选 \(x\) 的行的话,就相当于能选自环、二元环,如果要选,就相当于可以选一个链出来然后连一条 \(x\) 的边,正负只需要判断偶环个数即可。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define mod 1000000007
#define int long long
#define MAXN 1000005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
	int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
	return res;
}
int inv (int x){return qkpow (x,mod - 2);}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}

vector <int> g[MAXN];
int f[MAXN][6][2],h[MAXN][3][2];
int n,X,ans,d[MAXN],p[MAXN],b[MAXN],c[MAXN];

void dfs (int u){
	f[u][0][0] = d[u],f[u][1][0] = f[u][3][1] = mod - c[u],f[u][5][1] = mod - b[u],f[u][0][1] = X,h[u][0][0] = h[u][1][1] = h[u][2][1] = 1;
	for (Int v : g[u]){
		dfs (v);
		int tmp[6][2] = {};
		//合并二元环的情况
		for (Int k1 = 0;k1 < 2;++ k1)
			for (Int k2 = 0;k2 < 2;++ k2)
				if (!(k1 & k2)) 
					Add (tmp[2][k1 | k2],mul (h[u][0][k1],mul (b[v],f[v][1][k2]))),
					Add (tmp[2][k1 | k2],mul (f[u][2][k1],add (f[v][0][k2],add (f[v][2][k2],f[v][4][k2]))));
		//u选自环的情况
		for (Int k1 = 0;k1 < 2;++ k1)
			for (Int k2 = 0;k2 < 2;++ k2)
				if (!(k1 & k2)) 
					Add (tmp[0][k1 | k2],mul (f[u][0][k1],add (f[v][0][k2],add (f[v][2][k2],f[v][4][k2]))));
		//u开始二元环
		for (Int k1 = 0;k1 < 2;++ k1)
			for (Int k2 = 0;k2 < 2;++ k2)
				if (!(k1 & k2)) 
					Add (tmp[1][k1 | k2],mul (f[u][1][k1],add (f[v][0][k2],add (f[v][2][k2],f[v][4][k2]))));
		//u合并两个链
//		cout << "rnmd " << h[u][1][1] << " " << X << " " << f[v][5][1] << endl;
//		cout << "rnmd " << h[u][2][1] << " " << X << " " << f[v][3][1] << endl;
		Add (tmp[4][1],mul (X,mul (h[u][1][1],f[v][5][1]))),
		Add (tmp[4][1],mul (X,mul (h[u][2][1],f[v][3][1])));
//		cout << tmp[4][1] << endl;
//		cout << f[u][4][1] << " " << add (f[v][0][0],add (f[v][2][0],f[v][4][0])) << endl;
		Add (tmp[4][1],mul (f[u][4][1],add (f[v][0][0],add (f[v][2][0],f[v][4][0]))));
		//u开始链/继续v之外的儿子的链
		Add (tmp[3][1],mul (f[u][3][1],add (f[v][0][0],add (f[v][2][0],f[v][4][0])))),
		Add (tmp[5][1],mul (f[u][5][1],add (f[v][0][0],add (f[v][2][0],f[v][4][0]))));
		//u继续v的链
		Add (tmp[3][1],mul (h[u][0][0],mul (mod - c[u],f[v][3][1]))),
		Add (tmp[5][1],mul (h[u][0][0],mul (mod - b[u],f[v][5][1])));
		//u到子树内一个点的直上直下
		memcpy (f[u],tmp,sizeof (f[u]));
		int htmp[3][2] = {};
		for (Int k1 = 0;k1 < 2;++ k1)
			for (Int k2 = 0;k2 < 2;++ k2)
				if (!(k1 & k2))
					Add (htmp[1][k1 | k2],mul (h[u][0][k1],f[v][3][k2])),
					Add (htmp[2][k1 | k2],mul (h[u][0][k1],f[v][5][k2])),
					Add (htmp[1][k1 | k2],mul (h[u][1][k1],add (f[v][0][k2],add (f[v][2][k2],f[v][4][k2])))),
					Add (htmp[2][k1 | k2],mul (h[u][2][k1],add (f[v][0][k2],add (f[v][2][k2],f[v][4][k2])))),
					Add (htmp[0][k1 | k2],mul (h[u][0][k1],add (f[v][0][k2],add (f[v][2][k2],f[v][4][k2]))));
 		memcpy (h[u],htmp,sizeof (htmp));
	}
}

signed main(){   
	freopen ("B.in","r",stdin);
    freopen ("B.out","w",stdout);
	read (n,X);
	for (Int i = 1;i <= n;++ i) read (d[i]),Sub (d[i],X);
	for (Int i = 2;i <= n;++ i) read (p[i],b[i],c[i]),Sub (b[i],X),Sub (c[i],X);
	for (Int i = 2;i <= n;++ i) g[p[i]].push_back (i);
	dfs (1);int ans = 0;
	for (Int i = 0;i < 5;i += 2)
		for (Int k = 0;k < 2;++ k) Add (ans,f[1][i][k]);
	write (ans),putchar ('\n');
 	return 0;
}

取石子游戏

题目传送门

Description

Solution

可以看出的是,将一堆石子表示为 \((n,a,b)\),其中 \(n\) 表示石子个数,\(a\) 表示 Alice 可以选的个数,\(b\) 表示 Bob 可以选的个数。可以发现 \((n\%(a+b),a,b)\)\((n,a,b)\) 等价。

我们设 \(A_i\) 表示第 \(i\) 堆石子若 Alice 拿会产生的贡献次数,\(B_i\) 表示第 \(i\) 堆石子若 Bob 拿会产生的贡献次数,若 \(\max(a,b)>n\ge \min(a,b)\),那么只有一方可以拿到,这个可以直接储存下来,否则若 \(n\ge \max(a,b)\),那么谁先拿,你就可以霸占这一堆石子的贡献。

假如你是 Alice,你一定会想要 \(\sum A_{p_i}-SB+\sum_ B_{p_i}\),其中 \(p_i\) 表示 Alice 霸占的堆数。Bob 也同理。也就是说它们一定会按着 \(A_i+B_i\) 的顺序交替选,这个用权值线段树处理就好了。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define int long long
#define MAXN 100005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}

#define LOGN 31
#define ls(x) son[x][0]
#define rs(x) son[x][1]

int n,rt,tot,cnt[MAXN * LOGN],rig[MAXN * LOGN][2],son[MAXN * LOGN][2];

void pushup (int x){
	cnt[x] = cnt[ls(x)] + cnt[rs(x)];
	rig[x][0] = rig[rs(x)][0] + rig[ls(x)][0 ^ cnt[rs(x)] & 1];
	rig[x][1] = rig[rs(x)][1] + rig[ls(x)][1 ^ cnt[rs(x)] & 1];	
}

void modify (int &x,int l,int r,int pos){
	if (!x) x = ++ tot;
	if (l == r){
		cnt[x] ++,rig[x][0] = cnt[x] / 2 * l,rig[x][1] = (cnt[x] + 1) / 2 * l;
		return ;
	}
	int mid = (l + r) >> 1;
	if (pos <= mid) modify (ls(x),l,mid,pos);
	else modify (rs(x),mid + 1,r,pos);
	pushup (x);
}

void Solveit(){
	read (n);int res = 0;
	for (Int i = 1;i <= n;++ i){
		int X,A,B;
		read (X,A,B),X %= (A + B);
		int mn = min (A,B),mx = max (A,B);
		if (mn <= X && X < mx){
			if (A <= X) res += X / A;
			else res -= X / B;
		}
		else if (mx <= X) res += X / A,modify (rt,1,2e9,X / A + X / B);
		bool Ali = res - rig[rt][0] > 0,Bob = res - rig[rt][1] < 0;
		if (Ali && Bob) puts ("First");
		else if (!Ali && !Bob) puts ("Second");
		else if (Ali && !Bob) puts ("Alice");
		else puts ("Bob");
	}
}

signed main(){
	freopen ("C.in","r",stdin);
	freopen ("C.out","w",stdout);
	Solveit();
 	return 0;
}
posted @ 2021-07-07 21:45  Dark_Romance  阅读(43)  评论(0编辑  收藏  举报