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