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