2021.8.17考试总结[NOIP42]

$\huge{取模不能比大小!}$

$\huge{取模不能比大小!}$

$\huge{取模不能比大小!}$

有了打地鼠的前车之鉴,我深信树规板子是可以出现在联赛题里的。

所以T1十分钟码完直接溜了,后两道肉眼可见的不可做,最后半小时百无聊赖地胡乱打表,心想有二百差不多了,于是T1光速保龄。哈哈

赛后十分钟直接$rk3$超爽的好吗(哭

T1 卷


就像上文所说的。$DP$时带个对数加和就行。

$code:$

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int NN=2e5+5,p=1e9+7;
 5 int n,idx,to[NN<<1],nex[NN<<1],head[NN],w[NN];
 6 int f[NN][2];
 7 double l[NN][2];
 8 inline int read(){
 9     int x=0,f=1; char ch=getchar();
10     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
11     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
12     return x*f;
13 }
14 inline void write(int x,char sp){
15     char ch[20]; int len=0;
16     if(x<0){ putchar('-'); x=~x+1; }
17     do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x);
18     for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
19 }
20 inline void add(int a,int b){
21     to[++idx]=b; nex[idx]=head[a]; head[a]=idx;
22     to[++idx]=a; nex[idx]=head[b]; head[b]=idx;
23 }
24 void dfs(int s,int fa){
25     int w1=1,w2=1;
26     double l1=log(w[s]),l2=0;
27     for(int i=head[s];i;i=nex[i]){
28         int v=to[i]; 
29         if(v==fa) continue;
30         dfs(v,s);
31         (w1*=f[v][0])%=p; l1+=l[v][0];
32         (w2*=((l[v][1]>l[v][0])?f[v][1]:f[v][0]))%=p; l2+=max(l[v][1],l[v][0]);
33     }
34     f[s][1]=w1*w[s]%p; f[s][0]=w2;
35     l[s][1]=l1; l[s][0]=l2;
36 }
37 signed main(){
38     n=read();
39     for(int i=1;i<=n;i++) w[i]=read();
40     for(int a,b,i=1;i<n;i++){
41         a=read(); b=read();
42         add(a,b);
43     }
44     dfs(1,0);
45     if(l[1][1]<l[1][0]) write(f[1][0],'\n');
46     else write(f[1][1],'\n');
47     return 0;
48 }
T1

T2 简单题


不难发现其实就是选择奇数。偶数会随奇数被安排位置。

通过简单容斥可以算出奇数被选时的贡献对应的奇数数量,计算一共是$log$级的。

发现每个奇数选与不选都会有相应的贡献,且两者最多差$1$。可以求出贡献差$1$的奇数数量$x$以及奇数全被选时的集合大小$sum$。

于是每次询问中$m$与$sum$的差就是有几个贡献差$1$的奇数没有被选。贡献无差别的奇数选不选皆可,记这种奇数数量为$tmp$,答案为$C_x^{sum-m}\times 2^{tmp}$。

$code:$

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int p=1e7+19;
 5 int n,m,q,ext,sum,x,base,num[60],fac[p],inv[p];
 6 inline int read(){
 7     int x=0,f=1; char ch=getchar();
 8     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 9     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
10     return x*f;
11 }
12 inline void write(int x,char sp){
13     char ch[20]; int len=0;
14     if(x<0){ putchar('-'); x=~x+1; }
15     do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x);
16     for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
17 }
18 inline int qpow(int a,int b){
19     int res=1;
20     while(b){
21         if(b&1) res=res*a%p;
22         a=a*a%p;
23         b>>=1;
24     }
25     return res;
26 }
27 inline int cc(int n,int m){ if(n<m) return 0; return fac[n]*inv[m]%p*inv[n-m]%p; }
28 inline int C(int n,int m){
29     if(!m) return 1;
30     return C(n/p,m/p)*cc(n%p,m%p)%p;
31 }
32 signed main(){
33     n=read(); q=read(); sum=n+1>>1; ext=log(n)/log(4); x=sum-((n>>1)+1>>1);
34     fac[0]=inv[0]=1; 
35     for(int i=1;i<p;i++) fac[i]=fac[i-1]*i%p; inv[p-1]=qpow(fac[p-1],p-2);
36     for(int i=p-2;i;i--) inv[i]=inv[i+1]*(i+1)%p;
37     for(int i=1;i<=ext;i++){
38         int tmp=(n>>i)>>i;
39         num[i]=tmp+1>>1;
40         num[i-1]-=num[i];
41         x+=num[i]-(((tmp>>1)+1)>>1);
42     }
43     for(int i=1;i<=ext;i++) sum+=i*num[i];
44     base=qpow(2,(n+1>>1)-x);
45     while(q--){
46         m=read();
47         if(m>sum||m<sum-x){ puts("0"); continue; }
48         write(C(x,sum-m)*base%p,'\n');
49     }
50     return 0;
51 }
T2

T3 粉丝


把大于等于$x$与小于等于$y$的限制拆为大于等于$x$与大于等于$y$,计算两者后作差即可。于是转化为划分数$DP$。

直接$DP$复杂度$O(n^2)$,可以把$DP$拆为两部分,以$\sqrt n$为界。

前一部分用完全背包,后一部分用好像很经典的划分数$DP$。

$g_{i,j}$为划分为$i$个数,加和为$j$的方案数。有$g_{i,j}=g_{i-1,j}+g(i,j-i)$。前一部分是新增一个$0$,后一部分是对所有划分出的数$+1$。

由于单纯$DP$时不能保证下界,因此最后应将$g_{i,j}$转为$g_{i,j-i*\sqrt n}$,$DP$时要注意。

因为从$\sqrt n$开始,因此最多划分出$\frac{n}{\sqrt n}$个数。

最后把两个$DP$合并。

$code:$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 namespace IO{
 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(); }
 9         return x*f;
10     }
11     inline void write(int x,char sp){
12         char ch[20]; int len=0;
13         if(x<0){ putchar('-'); x=~x+1; }
14         do{ ch[len++]=x%10+(1<<5)+(1<<4); x/=10; }while(x);
15         for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
16     }
17 }using namespace IO;
18 
19 const int NN=1e9;
20 int n,p,x,y,b,B,f[100005],g[100005],G[100005];
21 
22 int calc(int dwn){
23     int ans=0;
24     if(dwn>n) return 0;
25     memset(f,0,sizeof(f));
26     memset(g,0,sizeof(g));
27     memset(G,0,sizeof(G));
28     b=max(B,dwn);
29     f[0]=g[0]=G[0]=1;
30     for(int i=dwn;i<b;i++)
31         for(int j=i;j<=n;j++) (f[j]+=f[j-i])%=p;
32     for(int i=1;i<=n/b;i++){
33         int tmp=i*b;
34         for(int j=i;j+tmp<=n;j++) (g[j]+=g[j-i])%=p;
35         for(int j=0;j+tmp<=n;j++) (G[j+tmp]+=g[j])%=p;
36     }
37     for(int i=0;i<=n;i++)
38         (ans+=(1ll*f[i]*G[n-i]%p))%=p;
39     return ans;
40 }
41 signed main(){
42     x=read(); y=read(); n=read(); p=read(); B=sqrt(n);
43     write((calc(x)-calc(y+1)+p)%p,'\n');
44     return 0;
45 }
T3

 


 

posted @ 2021-08-17 19:31  keen_z  阅读(55)  评论(0编辑  收藏  举报