T1
沒有問號的情況下,考慮如何線性判定。考慮每兩位當作一組,對於每組有如下兩種操作:
- 將兩位依次壓入棧中;
- 將第一位與棧中全部元素合併後,再將第二位壓入棧中。
可以發現棧中的情況可以看作是關於下一個壓入元素的函數,即 ,表示當 $x = 0 $時返回 , 時返回 。
假設當前棧中情況爲 ,考慮壓入兩位 ,容易根據上述兩種操作合併出新的函數。
由於本質不同的函數只有 種,用 表示前 位能否得到 。
對於計數問題, 套 即可,狀態數是 。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define pb push_back
#define mp make_pair
#define LL long long
#define db double
#define Ct const
#define mod 998244353
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res+ch-'0');
}
char s[maxn];
int f[2][2][2],n;
int g[2][1<<4];
void add(int &u,int v){ ((u+=v)>=mod) && (u -= mod); }
int main(){
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
int T;
for(scanf("%d",&T);T--;){
scanf("%s",s);
int cnt = 0;
rep(i,0,1) rep(j,0,1) rep(k,0,1) f[k][j][i] = s[cnt++] - '0';
memset(g,0,sizeof g);
scanf("%s",s+1);
n = strlen(s+1);
int now = 1 , pre = 0;
g[now][2] = 1;
for(int i=1;i+2<=n;i+=2){
swap(now,pre);
memset(g[now],0,sizeof g[now]);
rep(sta,0,(1<<4)-1) if(g[pre][sta])
rep(a,s[i]=='?'?0:s[i]-'0',s[i]=='?'?1:s[i]-'0')
rep(b,s[i+1]=='?'?0:s[i+1]-'0',s[i+1]=='?'?1:s[i+1]-'0')
{
int nsta = 0;
rep(c,0,1) rep(d,0,1) if(sta >> (2*c+d) & 1)
nsta |= 1 << 2 * (f[a][b][0] ? d : c) + (f[a][b][1] ? d : c),
nsta |= 1 << 2 * (a ? f[d][b][0] : f[c][b][0]) + (a ? f[d][b][1] : f[c][b][1]);
add(g[now][nsta],g[pre][sta]);
}
}
int ans = 0;
if(s[n] != '?'){
rep(sta,0,(1<<4)-1) if(g[now][sta]){
int f = 0;
rep(c,0,1) rep(d,0,1) if(sta >> (2*c+d) & 1)
if((s[n]-'0' ? d : c) == 1){
f = 1;
break;
}
if(f)
add(ans,g[now][sta]);
}
}
else{
s[n] = '0';
rep(sta,0,(1<<4)-1) if(g[now][sta]){
int f = 0;
rep(c,0,1) rep(d,0,1) if(sta >> (2*c+d) & 1)
if((s[n]-'0' ? d : c) == 1){
f = 1;
break;
}
if(f)
add(ans,g[now][sta]);
}
s[n] = '1';
rep(sta,0,(1<<4)-1) if(g[now][sta]){
int f = 0;
rep(c,0,1) rep(d,0,1) if(sta >> (2*c+d) & 1)
if((s[n]-'0' ? d : c) == 1){
f = 1;
break;
}
if(f)
add(ans,g[now][sta]);
}
}
printf("%d\n",ans);
}
}
T2
顯然答案是 的子串,所以可以把 的後綴自動機建出來,在上面考慮。
對於 endpos
集合相同的串,顯然 *
的個數隨着串的長度單調遞增,所以可以二分這個長度 ,然後 *
的個數就是每相鄰兩個 endpos
的距離和 取 並求和。
這個東西也許可以線段樹合併但沒有必要,(大概)更好寫的做法是在 fail 樹上做樹上啓發式合併,並用 set 和樹狀數組維護相鄰元素的距離。
最後爲了比較哪一個串最優,求這兩個串在後綴樹上的即可,所以一開始就反着建串即可。
#include<bits/stdc++.h>
#define maxn 40005
#define S 131
#define rep(i,j,k) for(register int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(register int i=(j),LIM=(k);i>=LIM;i--)
#define pb push_back
#define mp make_pair
#define LL long long
#define db double
#define Ct const
#define maxc 26
using namespace std;
char s[maxn];
int n,K;
int tr[maxn][maxc],fa[maxn],len[maxn],pos[maxn],tot,last;
LL hs[maxn],pw[maxn];
inline LL calc(int a,int b){ return (hs[b] - hs[a-1] * pw[b-a+1]); }
int info[maxn],Prev[maxn],to[maxn],cnt_e;
inline void Node(int u,int v){ Prev[++cnt_e]=info[u],to[cnt_e]=v,info[u]=cnt_e; }
void ins(int c){
int u = ++tot , p = last , q;
len[last = u] = len[p] + 1;
for(;p != -1 && tr[p][c] == 0;p=fa[p]) tr[p][c] = u;
if(p == -1) fa[u] = 0;
else if(len[q = tr[p][c]] == len[p] + 1) fa[u] = q;
else{
int v = ++tot;
memcpy(tr[v],tr[q],sizeof tr[q]),fa[v]=fa[q],len[v]=len[p]+1;
for(;p!=-1 && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = fa[u] = v;
}
}
int sz[maxn] , son[maxn];
void dfs0(int u){
son[u] = -1 , sz[u] = 1;
for(int i=info[u];i;i=Prev[i]){
int v = to[i];
dfs0(v);
if(son[u] == -1 || sz[v] > sz[son[u]]) son[u] = v;
sz[u] += sz[v];
}
}
LL tri[maxn],trx[maxn];
inline void upd(int u,LL v){ LL p = v * (1 - u);for(;u<=n;u+=u&-u) tri[u] += v , trx[u] += p; }
inline LL qry(int u){ LL r=0,I=u;for(;u;u-=u&-u) r+=tri[u] * 1ll * I + trx[u];return r; }
int ansl,ansr;
set<int>st;
void dfs2(int u){
if(pos[u]){
set<int>::iterator it = st.insert(pos[u]).first , it2 = it;
it2 ++ , it --;
upd(1,1) , upd(pos[u] - (*it) + 1 , -1);
if(it2 != st.end())
upd((*it2)-pos[u]+1,-1),upd((*it2)-(*it)+1,1);
}
for(int i=info[u];i;i=Prev[i]){
int v = to[i];
dfs2(v);
}
}
void dfs1(int u){
for(int i=info[u];i;i=Prev[i]){
int v = to[i];
if(v == son[u]) continue;
dfs1(v);
set<int>::iterator it = st.end() , it2 = st.end();
it--;
for(;it != st.begin();){
it2 = it , it2--;
upd(1,-1) , upd((*it)-(*it2)+1,1);
it--;
}
st.clear();
st.insert(0);
}
if(son[u] != -1)
dfs1(son[u]);
if(!u) return;
if(pos[u]){
set<int>::iterator it = st.insert(pos[u]).first , it2 = it;
it2 ++ , it --;
upd(1,1) , upd(pos[u] - (*it) + 1 , -1);
if(it2 != st.end())
upd((*it2)-pos[u]+1,-1),upd((*it2)-(*it)+1,1);
}
for(int i=info[u];i;i=Prev[i]){
int v = to[i];
if(v == son[u]) continue;
dfs2(v);
}
int L = len[fa[u]]+1 , R = len[u] , mid;
for(;L<R;){
mid = L+R >> 1;
if(qry(mid) < K) L = mid + 1;
else R = mid;
}
if(qry(L) == K){
int sr = (*st.rbegin()) , sl = sr - L + 1;
if(sr - sl < ansr - ansl) ansl = sl , ansr = sr;
else if(sr - sl == ansr - ansl){
L = 0 , R = sr - sl;
for(;L<R;){
mid = L+R+1 >> 1;
if(calc(sl,sl+mid-1) != calc(ansl,ansl+mid-1)) R = mid - 1;
else L = mid;
}
if(s[sl+L] < s[ansl+L])
ansl = sl , ansr = sr;
}
}
}
int main(){
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
int T;fa[0] = -1;
pw[0] = 1;
rep(i,1,maxn-1) pw[i] = pw[i-1] * S;
for(scanf("%d",&T);T--;){
last = 0 , ansr = 0x3f3f3f3f , ansl = 0;
memset(trx,0,sizeof trx);
memset(tri,0,sizeof tri);
memset(pos,0,sizeof pos);
memset(tr,0,sizeof tr);
st.clear();
scanf("%s",s+1);
scanf("%d",&K);
n = strlen(s+1);
rep(i,1,n) hs[i] = (hs[i-1] * S + s[i]) , ins(s[i] - 'a') , pos[last] = i;
rep(i,1,tot) Node(fa[i],i);
dfs0(0);
st.insert(0);
dfs1(0);
if(ansr > n) puts("NOTFOUND!");
else{
rep(i,ansl,ansr) putchar(s[i]);
putchar('\n');
}
rep(i,0,tot) info[i] = 0;
cnt_e = 0;
tot = 0;
}
}
T3
首先,考慮到區間異或可以前綴和。我們把分割序列的斷點取出來,那麼就會得到一個分割方案的必要條件,即每個斷點處的前綴和必須是給出的集合能夠線性組合出來的數。
例如 的前綴和爲。則對於集合 和分割方案,則有前綴和 。於是,能夠馬上得到一個結論:總是存在一種方案,使得最後構造出來的序列段數 。
證明就是考慮到如果最終方案中有兩個斷點前綴和相同,例如 ,那麼顯然後三段的異或和爲,顯然是可以和前面的 並起來變成單個 。從最簡單的 開始考慮。 表示最後一個斷點在 是否可行。根據上面的結論,注意到如果有前綴和 ,且有 ,那麼顯然 $f_i $ 能貢獻到的狀態, 都能貢獻到。
於是實際上我們只需要記錄在所有的可行分割方案中,一個前綴和的出現位置的最小值 ,如果不存在則設爲 。容易證明最終的可行性就是 。一個值分別異或上查詢集合裏所有的數,可以得到這個數的轉移目標的集合。
我們把序列前綴和的每種數單獨提出來寫成若干個位置集合,如果可以支持查詢後繼,就能轉移:注意到對一個值 如果有 ,且在 處查詢過,如果沒有後繼,則在 處沒有後繼;如果有後繼,顯然 $ j$ 處的沒有 處的優。於是可以用類似最短路的寫法,用一個優先隊列 ,每種能線性組合出的數最多訪問一次,所以單次詢問最多調用 次查詢後繼操作。
剩下的問題是區間異或,查詢一個值在序列中下一次出現位置。可以使用分塊的技巧處理。對於每一塊裏,維護一個 記錄哪些數出現過,和一個異或標記。這樣修改和詢問的時候都暴力即可。
雖然複雜度 有點爆炸,但出題人很菜,肯定卡不滿,所以就可以跑過去了。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define pb push_back
#define mp make_pair
#define LL long long
#define db double
#define Ct const
#define mp make_pair
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
char Start;
#define S 355
int n,Q,A[maxn],sm[maxn],id[maxn],st[S],ed[S];
bitset< 1<<20 >B[S];
int tg[S],f[1<<20];
int findnxt(int u,int v){
rep(i,u,ed[id[u]]) if((sm[i] ^ tg[id[i]]) == v)
return i;
rep(i,id[u]+1,id[n])
if(B[i][v ^ tg[i]])
rep(j,st[i],ed[i])
if((sm[j] ^ tg[i]) == v)
return j;
return n+1;
}
int main(){
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
read(n),read(Q);
rep(i,1,n) read(A[i]),sm[i] = sm[i-1] ^ A[i],id[i] = (i-1)/S , st[id[i]] = st[id[i]] ? st[id[i]] : i , ed[id[i]] = i;
rep(i,1,n) B[id[i]][sm[i]] = 1;
for(int op,x,y;Q--;){
read(op),read(x);
if(op == 1){
read(y);int t = A[x] ^ y;
rep(i,id[x]+1,id[n]) tg[i] ^= t;
int p = tg[id[x]];tg[id[x]] = 0;
B[id[x]].reset();A[x] = y;
rep(i,st[id[x]],ed[id[x]])
sm[i] ^= p ^ (i >= x ? t : 0),
B[id[x]][sm[i]] = 1;
}
else{
static int ar[10],st[1<<5];
rep(i,0,x-1) read(ar[i]);
rep(i,0,(1<<x)-1){
st[i] = 0;
rep(j,0,x-1) if(i >> j & 1)
st[i] ^= ar[j];
f[st[i]] = n+1;
}
static priority_queue<pair<int,int> >q;
rep(i,0,x-1){
int t;
if((t = findnxt(0,ar[i])) <= n)
f[ar[i]] = t , q.push(mp(-f[ar[i]],ar[i]));
}
for(int u,w;!q.empty();){
w = q.top().first , u = q.top().second;q.pop();
if(-w != f[u]) continue;
int t ;
rep(j,0,x-1) if((t = findnxt(-w , ar[j] ^ u)) < f[ar[j] ^ u]){
f[ar[j]^u] = t;
q.push(mp(-f[ar[j]^u],ar[j]^u));
}
}
bool flg = 0;
rep(i,0,(1<<x)-1) if(st[i] == (sm[n] ^ tg[id[n]]) && f[st[i]] <= n){
flg = 1;
break;
}
if(flg) puts("yes");
else puts("no");
}
}
}