文章目錄
A Stock Arbitraging
暴力
B Tiling Challenge
暴力
1A&C Prefix Sum Primes
先放2,再放一個1,之後把所有的2放完,再放1,除了2,其他的質數都是奇數
1B&D - Three Religions
序列自動機+dp
分析:給定三個空串和一個母串
操作1:選一個串在其後面增加一個字符
操作2:刪除一個串最後的一個字符
總結,關於序列匹配首先想到序列自動機肯定沒錯,關鍵是這個m^3的dp不好想,div2 最後也沒過幾個
分析:
表示匹配 最少需要到母串的哪個位置
const int maxn = 3e5+10,maxm = 250+10;
char ar[maxn],s[maxm][4];
int len[3];
int nxt[maxn][30];
int dp[maxm][maxm][maxm];
char op[3],ch[maxn];
int main(void)
{
int n,m;cin>>n>>m;
cin>>(ar+1);
for(int i = 0;i < 26; ++i) nxt[n][i] = nxt[n+1][i] = n+1;
for(int i = n-1;i >= 0; --i){
for(int j = 0;j < 26; ++j)
nxt[i][j] = nxt[i+1][j];
nxt[i][(int)(ar[i+1]-'a')] = i+1;
}
for(int i = 1;i <= m; ++i){
int t;
scanf("%s %d",op,&t);
t--;
// cout<<t<<endl;
if(op[0] == '-'){
len[t]--;
}
else{
scanf("%s",ch);
// cout<<len[t]<<endl;
++len[t];
// cout<<len[t]<<endl;
// cout<<len[0]<<" "<<len[1]<<" "<<len[2]<<endl;
s[len[t]][t] = ch[0];
// 初始化
// DEBUG
for(int i = t == 0?len[0]:0;i <= len[0]; ++i){
for(int j = t == 1?len[1]:0;j <= len[1]; ++j){
for(int k = t == 2?len[2]:0;k <= len[2]; ++k){
// cout<<i<<" "<<j<<" "<<k<<endl;
dp[i][j][k] = n+1;
}
}
}
// DEBUG;
for(int i = t == 0?len[0]:0;i <= len[0]; ++i){
for(int j = t == 1?len[1]:0;j <= len[1]; ++j){
for(int k = t == 2?len[2]:0;k <= len[2]; ++k){
if(i > 0)
dp[i][j][k] = min(dp[i][j][k],nxt[dp[i-1][j][k]][s[i][0]-'a']);
if(j > 0)
dp[i][j][k] = min(dp[i][j][k],nxt[dp[i][j-1][k]][s[j][1]-'a']);
if(k > 0)
dp[i][j][k] = min(dp[i][j][k],nxt[dp[i][j][k-1]][s[k][2]-'a']);
}
}
}
}
// cout<<dp[len[0]][len[1]][len[2]]<<endl;
puts(dp[len[0]][len[1]][len[2]] <= n ?"YES":"NO");
}
return 0;
}
div1 E Election Promises
題意:
給定一棵有向樹,每個點有一個權值,兩個人進行操作,可以選擇任意一個節點,將其權值減小爲一個非負數,並且可以將其子節點修改爲任意的權值,判定是否先手必贏,如果先手必贏,給定一個先手的方案
分析:
1 我們看到有向樹,就可以聯想到拓撲排序有關的問題,或者直接dfs進行圖的遍歷,
2 博弈問題,我們應該關注其是不是一個ICG(組合博弈問題),能否使用sg函數找出其規律並求解,當你找出了sg函數的時候,你就已經可以解決這個問題了
3 大部分博弈問題的模型都或多或少與石子博弈問題有關,本題同樣,輸出方案的過程就是石子博弈問題輸出方案的翻版
本題的SG函數模型,我們可以這樣來構造,即
一個節點的Sg函數值,就是mex(sg[son])
那麼判斷先手必勝的條件是什麼呢?,
1 我們首先想到博弈問題有關的樹博弈問題,根節點的sg值就是整個遊戲的sg值,這樣想,樣例就不對
2 將所有節點的值Sg函數值都異或起來,看起來挺有效的,但是也不太對
3 我們思考如果只有兩個點的情況,如果沒有邊,那麼就是石子博弈問題,直接取sumxor(h(x)) 就ok了,對於這一題,我們同樣需要異或,就是將所有相同sg值的點的h值異或,如果有一個異或值部位0,那麼肯定必贏
怎麼輸出方案呢?
首先我們參考有關問題的方案輸出,那就是我們找到最大的sg函數相同的異或不爲0的位置,如果 xorsum[sg[u]] ^ h[i] < h[i] ,說明這個時候就是可行的,它的子節點包含所有的sg值,可以同時將它的子節點的xor[sg[son]]都修改,這樣最後肯定所有 的xor[sg[i]] 都爲0,就是必敗態
const int maxn = 2e5+10;
vector<int> G[maxn];
int topo[maxn];
int sg[maxn],deg[maxn],mem[maxn];
int Xor[maxn];
LL h[maxn];
int main(void)
{
// IOS;
int n,m;cin>>n>>m;
for(int i = 1;i <= n; ++i) scanf("%lld",&h[i]);
// cin>>h[i];
for(int i = 1;i <= m; ++i){
int u,v;
//cin>>u>>v;
scanf("%d%d",&u,&v);
G[u].Pb(v);
deg[v]++;
}
queue<int> q;
int cnt = 1;
for(int i = 1;i <= n; ++i)
if(deg[i] == 0)
q.push(i);//,vis[i] = true;
while(!q.empty()){
int p = q.front(); q.pop();
topo[cnt++] = p;
for(auto c: G[p]){
deg[c]--;
if(deg[c] == 0)
q.push(c);
}
}
for(int i = n;i >= 1; --i){
int u = topo[i];
for(auto c: G[u])
mem[sg[c]] = i;
while(mem[sg[u]] == i)
sg[u]++;
Xor[sg[u]] ^= h[u];
}
// for(int i = 1;i <= n; ++i)
// cout<<sg[i]<<" ";
// cout<<endl;
int index = -1;
for(int i = 0;i < maxn; ++i)
if(Xor[i] != 0)
index = i;
if(index == -1)
return 0*puts("LOSE");
for(int i = 1;i <= n; ++i){
if(sg[i] == index && ((h[i] ^ Xor[index]) < h[i])){
h[i] ^= Xor[index];
for(auto c: G[i]){
h[c] ^= Xor[sg[c]];
Xor[sg[c]] = 0;
}
break;
}
}
// for
puts("WIN");
for(int i = 1;i <= n; ++i)
printf("%lld ",h[i]);
return 0;
}