20200704模擬賽

T1

sub1sub1

沒有問號的情況下,考慮如何線性判定。考慮每兩位當作一組,對於每組有如下兩種操作:

  1. 將兩位依次壓入棧中;
  2. 將第一位與棧中全部元素合併後,再將第二位壓入棧中。
    可以發現棧中的情況可以看作是關於下一個壓入元素的函數,即 G[a,b](x)G[a, b](x),表示當 $x = 0 $時返回 aax=1x = 1 時返回 bb

假設當前棧中情況爲 G[a,b](x)G[a, b](x),考慮壓入兩位 c,dc, d,容易根據上述兩種操作合併出新的函數G[a,b](x)G[a′ , b′ ](x)

由於本質不同的函數只有 44 種,用 f[i][j0][j1]f[i][j_0][j_1] 表示前 ii 位能否得到 G[j0,j1](x)G[j_0, j_1](x)

sub2sub2

對於計數問題,dpdpdpdp 即可,狀態數是 24n2^{4} n

AC Code\mathcal AC \ Code

#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

顯然答案是 ss 的子串,所以可以把 ss 的後綴自動機建出來,在上面考慮。

對於 endpos 集合相同的串,顯然 * 的個數隨着串的長度單調遞增,所以可以二分這個長度 lenlen,然後 * 的個數就是每相鄰兩個 endpos 的距離和 lenlenmin\min 並求和。

這個東西也許可以線段樹合併但沒有必要,(大概)更好寫的做法是在 fail 樹上做樹上啓發式合併,並用 set 和樹狀數組維護相鄰元素的距離。

最後爲了比較哪一個串最優,求這兩個串在後綴樹上的lcplcp即可,所以一開始就反着建串即可。

AC Code\mathcal AC \ Code

#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

首先,考慮到區間異或可以前綴和。我們把分割序列的斷點取出來,那麼就會得到一個分割方案的必要條件,即每個斷點處的前綴和必須是給出的集合能夠線性組合出來的數。

例如[1,1,4,5,1,4][1 , 1 , 4 , 5 , 1 , 4] 的前綴和爲[1,0,4,1,0,4][1 , 0 , 4 , 1 , 0 , 4]。則對於集合 {1,4}\{ 1 , 4 \} 和分割方案[1],[1],[4,5],[1],[4][1] , [1] , [4 , 5] , [1] , [4],則有前綴和 1,0,1,0,41 , 0 , 1 , 0 , 4。於是,能夠馬上得到一個結論:總是存在一種方案,使得最後構造出來的序列段數 m2km \leq 2^ k

證明就是考慮到如果最終方案中有兩個斷點前綴和相同,例如 [5,3,2,5][5 , 3 , 2 , 5],那麼顯然後三段的異或和爲00,顯然是可以和前面的 55 並起來變成單個 55。從最簡單的 dpdp 開始考慮。 fif_ i 表示最後一個斷點在 ii 是否可行。根據上面的結論,注意到如果有前綴和 pj=pi(j<i)p_j = p_i ( j \lt i ),且有 fj=1f _j = 1,那麼顯然 $f_i $ 能貢獻到的狀態, fjf_j 都能貢獻到。

於是實際上我們只需要記錄在所有的可行分割方案中,一個前綴和的出現位置的最小值 fvf_ v ,如果不存在則設爲 n+1n + 1。容易證明最終的可行性就是 fpnnf _{p _n} \leq n 。一個值分別異或上查詢集合裏所有的數,可以得到這個數的轉移目標的集合。

我們把序列前綴和的每種數單獨提出來寫成若干個位置集合,如果可以支持查詢後繼,就能轉移:注意到對一個值 vv 如果有 i<ji < j,且在 ii 處查詢過,如果沒有後繼,則在 jj處沒有後繼;如果有後繼,顯然 $ j$ 處的沒有 ii 處的優。於是可以用類似最短路的寫法,用一個優先隊列 bfsbfs,每種能線性組合出的數最多訪問一次,所以單次詢問最多調用 O(k2k)O (k2 ^ k ) 次查詢後繼操作。

剩下的問題是區間異或,查詢一個值在序列中下一次出現位置。可以使用分塊的技巧處理。對於每一塊裏,維護一個 bitsetbitset 記錄哪些數出現過,和一個異或標記。這樣修改和詢問的時候都暴力即可。

雖然複雜度 O(nk2kn)O(nk2^k\sqrt n) 有點爆炸,但出題人很菜,肯定卡不滿,所以就可以跑過去了。

AC Code\mathcal AC \ Code

#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");
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章