1. 程式人生 > 其它 >2021-07-06 集訓題解

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