Noip模擬44 2021.8.19
比較驚人的排行榜
更不用說爆零的人數了,為什麼聯賽會這麼難!!害怕了
還要再努力鴨
T1 Emotional Flutter
考場上沒切掉的神仙題
考率如何貪心,我們把黑色的條延長$s$,白色的縮短$s$,這樣把$jiao$的長度變成一
方便做,然後如果黑條長度大於$k$顯然不合法,直接判出
然後考慮將黑條左右範圍對$k$取模,然後發現這個答案和起始的位置有一一對應的關係
但是他並不是恰好對應的,即起始點是$0$的時候取模對應的值是$7$,這樣我感覺很麻煩
於是將黑條的起始點移動到$k-1$,這樣對應的起始點位置為$1~k-1$,方便理解
然後如果對黑條的左右端點取模發現$l>r$,那麼他對應的區間是$[0,r],[l,k-1]$
最後排個序看看能否有一個點不被所有區間覆蓋即可
細節很多,改題的時候改崩潰了
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='View Code0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}13 }using namespace AE86; 14 15 const int NN=5e5+5,inf=1e15; 16 int s,k,n,T,pos[NN],tmp,a[NN]; 17 bool f; 18 struct SNOW{int l,r;}line[NN]; 19 inline bool cmp(SNOW a,SNOW b){return a.l==b.l? a.r<b.r: a.l<b.l;} 20 21 namespace WSN{ 22 inline short main(){ 23 T=read(); 24 while(T--){ 25 s=read();k=read();n=read();f=0;tmp=0; 26 if(k<s){puts("NIE");continue;} pos[0]=1; 27 for(int i=1;i<=n;i++){ 28 a[i]=read(); 29 pos[i]=pos[i-1]+a[i]; 30 } 31 for(int i=1;i<=n;i++) if(i&1){ 32 int l=pos[i-1]+1,r=pos[i]+s-1; 33 if(r-l+1>=k){puts("NIE");f=1;break;} 34 l%=k; r%=k; 35 if(l>r){ 36 line[++tmp]=(SNOW){0,r}; 37 line[++tmp]=(SNOW){l,k-1}; 38 }else line[++tmp]=(SNOW){l,r}; 39 } 40 if(f) continue; 41 sort(line+1,line+tmp+1,cmp); 42 int i=1,maxn=0; bool fini=0; 43 if(line[1].l>0){puts("TAK");continue;} 44 for(int i=1;i<=tmp;i++){ 45 if(maxn+1<line[i].l) fini=1; 46 maxn=max(maxn,line[i].r); 47 }if(maxn<k-1) fini=1; 48 puts(fini?"TAK":"NIE"); 49 } 50 return 0; 51 } 52 } 53 signed main(){return WSN::main();}
T2 Medium Counting
神仙$dp$題,拍過的。。。
設$dp_{l,r,p,c}$表示從第$l$個串到第$r$個串,強制字尾第$p$位至少是$c$的方案數
然後記憶化搜尋每次列舉$l~r$區間內的串,轉移的時候分別討論加字尾或者提升字元字典序即可
狀態定義是真的神仙,不過$dp$還是比較好打的
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 15 const int mod=990804011; 16 int n,dp[55][55][30][30],len[55],a[55][25],maxn; 17 char s[55][25]; 18 19 inline int dfs(int l,int r,int p,int c){ 20 if(l>r) return dp[l][r][p][c]=1; 21 if(dp[l][r][p][c]!=-1) return dp[l][r][p][c]; 22 if(p>maxn) return dp[l][r][p][c]=(l==r); 23 if(c>26) return dp[l][r][p][c]=0; 24 dp[l][r][p][c]=dfs(l,r,p,c+1); 25 for(int i=l;i<=r;i++){ 26 if(!(a[i][p]==c || (c&&a[i][p]==27))) break; 27 (dp[l][r][p][c]+=dfs(l,i,p+1,0)*dfs(i+1,r,p,c+1)%mod)%=mod; 28 } 29 return dp[l][r][p][c]; 30 } 31 32 namespace WSN{ 33 inline short main(){ 34 n=read();memset(dp,-1,sizeof(dp)); 35 for(int i=1;i<=n;i++){ 36 scanf("%s",s[i]+1); 37 len[i]=strlen(s[i]+1); 38 maxn=max(maxn,len[i]); 39 for(int j=1;j<=len[i];j++){ 40 a[i][j]=s[i][j]-'a'+1; 41 if(s[i][j]=='?') a[i][j]=27; 42 } 43 }write(dfs(1,n,1,0)); 44 return 0; 45 } 46 } 47 signed main(){return WSN::main();}View Code
T3 Huge Counting
又是$dp$。。
考率題意可以轉化為(如果他不$%2$)從(1,1)走到(i,j)的步數
不難發現這個數是可重級排列:$\frac{(\sum_{i=1}^{n}x_i)!}{\prod_{i=1}^{n}(x_i!)}$
那麼對其取模$2$,表示這個數是奇數還是偶數,那麼找分子分母的$2$的出現次數即可
有這樣一個蝨子:$\sum_{w=2^i} (\lfloor \frac{\sum_{x_i}}{w} \rfloor-\sum{\lfloor \frac{x_i}{w}\rfloor})$
然後我們就發現,如果有相加的時候有進位,那麼它就會對下一位有貢獻,而這一位的貢獻不變。這意味著上式的值至少加$1$
所以,若要它等於$0$,一定要保證相加的時候沒有進位,也就是每一位上為$1$1的數至多有$1$個
那麼考慮$dp$,數位$dp$這樣子
設$dp_{pos,S}$表示從高到低$dp$到第$pos$位,然後$S$是貼上界的狀態
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 15 const int p=990804011; 16 int T,k,dp[70][600],l[10],r[10],lim[10],ans; 17 inline int DP(int pos,int S){ 18 if(dp[pos][S]!=-1) return dp[pos][S]; 19 int ans=0; 20 for(int i=0;i<k;i++)if( (!(S&(1ll<<i))) || (lim[i]&(1ll<<pos-1)) ){ 21 int sta=S; 22 for(int j=0;j<k;j++) if( i!=j && (lim[j]&(1ll<<pos-1)) ) sta^=S&(1<<j); 23 ans+=DP(pos-1,sta); 24 }int sta=S; 25 for(int j=0;j<k;j++) if(lim[j]&(1ll<<pos-1)) sta^=S&(1<<j); 26 ans+=DP(pos-1,sta); return dp[pos][S]=ans%p; 27 } 28 inline int ask(){ 29 for(int i=0;i<k;i++) if(lim[i]<0) return 0; 30 memset(dp,-1,sizeof(dp)); 31 for(int i=0;i<(1<<k);i++) dp[0][i]=1; 32 return DP(63,(1ll<<k)-1); 33 } 34 namespace WSN{ 35 inline short main(){ 36 T=read(); 37 while(T--){ 38 k=read();ans=0; 39 for(int i=1;i<=k;i++) l[i]=read(),r[i]=read(); 40 for(int i=0;i<(1<<k);i++){ 41 int cnt=0; 42 for(int j=0;j<k;j++) 43 if(i&(1<<j)) ++cnt, lim[j]=l[j+1]-2; 44 else lim[j]=r[j+1]-1; 45 ans+=ask()*((cnt&1)?-1:1); 46 }write((ans%p+p)%p); 47 } 48 return 0; 49 } 50 } 51 signed main(){return WSN::main();}View Code
T4 字元消除2
考場上發現是$kmp$,但是構造部分不大會,據說看懂題就很神了
然後衝了個暴力,美滋滋~
正解是先處理出原串的$kmp$,
那麼他的可行$t$集合是$n-nxt_i,n-nxt_{nxt_i},.......$
然後考慮構造一個和原串的$nxt$陣列一樣的$01$串
首先把處理出來的$nxt$迭代陣列找個最小的,如果他大於1
表示最短串的$bouder$不是$1$,那麼如果它全是$0$,則不會構造合法,
為了保證字典序,只需向$pre[1]$的位置新增一個$1$即可
然後我們可以列舉剛才迭代到沒有的$nxt$,表示你這個$01$串構造到$pre_i$的時候他的$nxt_i$指向$pre_{i-1}$
考慮兩種情況,如果$pre_i-pre_{i-1}>pre_{i-1}$,這個時候直接把原來的串當作字首複製到最後,中間沒有填滿的
用$0$填上,但是這樣可能不合法,因為中間放$0$可能會導致$kmp$偏移到這段$0$中的某一個,這時我們跑構造串的$kmp$
直接檢視他的$nxt$是否指向$pre_{i-1}$即可
另一種就是你複製的時候會和前面的串懟到一起,這回就需要直接在後面接原來串的字尾即可,正確性可以$yy$得到
更加詳細的講解可以看$學長部落格$
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int NN=2e5+5; 5 int T,n,nxt[NN],pre[NN],cnt,ans[NN],p,nt[NN]; 6 char s[NN],ch[NN]; 7 8 inline void kmp(){ 9 memset(nxt,0,sizeof(nxt)); 10 for(int i=2,j=0;i<=n;i++){ 11 while(j&&s[i]!=s[j+1]) j=nxt[j]; 12 if(s[i]==s[j+1]) ++j; 13 nxt[i]=j; 14 } 15 } 16 inline void Kmp(int n){ 17 memset(nt,0,sizeof(nt)); 18 for(int i=2,j=0;i<=n;i++){ 19 while(j&&ans[i]!=ans[j+1]) j=nt[j]; 20 if(ans[i]==ans[j+1]) ++j; 21 nt[i]=j; 22 } 23 } 24 25 namespace WSN{ 26 inline short main(){ 27 cin>>T; 28 while(T--){ 29 scanf("%s",s+1);n=strlen(s+1);kmp(); 30 cnt=0;memset(pre,0,sizeof(pre));memset(ans,0,sizeof(ans)); 31 int i=n; pre[++cnt]=n;while(nxt[i]){pre[++cnt]=nxt[i];i=nxt[i];} 32 reverse(pre+1,pre+cnt+1); if(pre[1]>1) ans[pre[1]]=1; 33 // for(int i=1;i<=n;i++) cout<<nxt[i]<<" ";cout<<endl; 34 for(int i=2;i<=cnt;i++) 35 if(pre[i-1]*2>=pre[i]) 36 for(int j=pre[i-1]+1;j<=pre[i];j++) 37 ans[j]=ans[j+pre[i-1]-pre[i]]; 38 else{ 39 int len=pre[i]-pre[i-1]; 40 for(int j=1;j<=pre[i-1];j++) ans[j+len]=ans[j]; 41 Kmp(pre[i]);if(nt[pre[i]]!=pre[i-1]) ans[len]=1; 42 } 43 for(int i=1;i<=n;i++) printf("%d",ans[i]);puts(""); 44 } 45 return 0; 46 } 47 } 48 signed main(){return WSN::main();}View Code