Noip模拟44 2021.8.19

时间:2021-08-20
本文章向大家介绍Noip模拟44 2021.8.19,主要包括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>='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

原文地址:https://www.cnblogs.com/hzoi-wsn/p/15164886.html