1. 程式人生 > 其它 >Noip模擬44 2021.8.19

Noip模擬44 2021.8.19

T1 Emotional Flutter T2 Medium Counting T3 Huge Counting T4 字元消除2

比較驚人的排行榜

更不用說爆零的人數了,為什麼聯賽會這麼難!!害怕了

還要再努力鴨

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>='
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 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();}
View Code

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