2021.9.13考试总结[NOIP模拟52]
T1 路径
考虑每一位的贡献,第$i$位每$2^i$个数会变一次,那么答案为$\sum_{i=1}^{log_2n} \frac{n}{2^i}$。
$code:$
1 #include<bits/stdc++.h> 2 #define int unsigned long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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[50]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=70; 26 int x,n,ext,ans; 27 28 signed main(){ 29 x=n=read(); 30 while(n){ ++ext; n>>=1; } 31 for(int i=0;i<ext;i++) ans+=x/(1ll<<i); 32 write(ans,'\n'); 33 return 0; 34 }
考场思路:
考虑式子实际意义,其实为每个数末尾连续$1$的个数加一的和。
于是可以数位$DP$。
但我没有。考虑上界$n-1$,扫描每一二进制位,将每个$1$变成$0$,都会给答案带来贡献。
具体来说,由低数第$i$为是$1$,会给末尾有$j$个连续$1$的数的个数带来$2^(i-j)$的贡献。最后容斥,统计答案。
注意如果扫到的$1$到末尾一直是$1$,则会给当前位带来额外$1$的贡献。以及
左移要写$1LL$!!
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=70; 26 int x,n,ans,ext,sum,lmt[NN],num[NN]; 27 bool link; 28 29 signed main(){ 30 x=ans=n=read(); --n; --x; link=1; 31 while(x){ 32 lmt[++ext]=x&1; 33 x>>=1; 34 } 35 for(int i=1;i<=ext;i++){ 36 if(!lmt[i]){ link=0; continue;} 37 if(lmt[i]) for(int j=1;j<i;j++) num[j]+=1ll<<(i-j-1); 38 if(link) ++num[i]; 39 } 40 for(int i=ext;i;i--){ 41 num[i]-=sum; 42 sum+=num[i]; 43 ans+=i*num[i]; 44 } 45 write(ans,'\n'); 46 return 0; 47 }
T2 赌神
大力推式子(但我不会
玄学做法:
考虑自己的最优方案,一定是要尽力令颜色数减少,但同时自己不能亏损,所以每次应按比例下注。
幕后黑手要尽力使颜色数不减少,所以每次投出颜色最多的球。
模拟即可。
其实由于自己每次取最优策略,不管幕后黑手怎样处理结果都不会改变。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=1e6+5,p=998244353; 26 int n,ans,sum,num; 27 priority_queue<int>q; 28 29 inline int inv(int x){ 30 int res=1,b=p-2; 31 while(b){ 32 if(b&1) res=res*x%p; 33 x=x*x%p; 34 b>>=1; 35 } 36 return res; 37 } 38 39 signed main(){ 40 n=read(); ans=1; 41 for(int i=1;i<=n;i++){ 42 num=read(); 43 sum+=num; 44 q.push(num); 45 } 46 while(sum){ 47 int x=q.top(); q.pop(); 48 if(x-1) q.push(x-1); 49 ans=ans*n%p*x%p*inv(sum)%p; 50 --sum; 51 } 52 write(ans,'\n'); 53 return 0; 54 }
T3 路径
数据太弱,$O(n^2log)$点分治都能A。。
点分治带个$fft$或$ntt$可以到$O(nlog^2)$,可A。但不会。
正解斯特林数+换根$DP$。
$x^k=\sum_{i=1}^k \begin{Bmatrix}k\\ i\end{Bmatrix}\times x^{\underline i}$
有$(x+1)^{\underline i}=i\times x^{\underline{i-1}}+x^{\underline i}$。
设$f_{i,j}$为$i$子树中所有点到$i$距离的$j$次下降幂之和,$g_{i,j}$为$i$子树中所有点到$i$父亲距离的$j$次下降幂之和。那么有
$f_{i,j}=\sum_{u\in son_i}g_{u,j}$,$g_{i,j}=j\times f_{i,j-1}+f_{i,j}$。
换根时考虑原来根的$f$,减去新根的$g$;新根的$f$加上由原来根的$f$通过上面式子算出的$g$。
$code:$
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 namespace IO{ 5 inline int read(){ 6 char ch=getchar(); int x=0,f=1; 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<<4)+(1<<5); x/=10; }while(x); 15 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 16 } 17 inline int max(int x,int y){ return x<y?y:x; } 18 inline int min(int x,int y){ return x<y?x:y; } 19 inline void swap(int &x,int &y){ x^=y^=x^=y; } 20 inline void chmax(int &x,int y){ x=x<y?y:x; } 21 inline void chmin(int &x,int y){ x=x<y?x:y; } 22 } using namespace IO; 23 24 const int NN=1e6+5,p=998244353,inv2=499122177; 25 int n,k,ans,f[NN][105],g[NN][105],sum[105]; 26 int idx,to[NN<<1],nex[NN<<1],head[NN],s[105][105]; 27 28 inline void add(int a,int b){ 29 to[++idx]=b; nex[idx]=head[a]; head[a]=idx; 30 to[++idx]=a; nex[idx]=head[b]; head[b]=idx; 31 } 32 33 void init(){ 34 s[1][1]=1; 35 for(int i=2;i<=k;i++) for(int j=1;j<=i;j++) 36 s[i][j]=(s[i-1][j-1]+1ll*j*s[i-1][j]%p)%p; 37 } 38 39 void dfs(int st,int fa){ 40 int ext=0; 41 for(int i=head[st];i;i=nex[i]){ 42 int v=to[i]; 43 if(v==fa) continue; 44 dfs(v,st); 45 for(int i=0;i<=k;i++){ 46 if(!g[v][i]) break; 47 chmax(ext,i); 48 (f[st][i]+=g[v][i])%=p; 49 } 50 } 51 ++f[st][0]; ext=min(k,ext+1); 52 for(int i=ext;i;i--) 53 g[st][i]=(1ll*i*f[st][i-1]%p+f[st][i])%p; 54 g[st][0]=f[st][0]; 55 } 56 57 void getans(int st,int fa){ 58 int ext=0,tmp[105]; 59 for(int i=0;i<=k;i++){ 60 if(!f[st][i]) break; 61 chmax(ext,i); 62 (sum[i]+=f[st][i])%=p; 63 } 64 ext=min(k,ext+1); 65 for(int i=head[st];i;i=nex[i]){ 66 int v=to[i]; 67 if(v==fa) continue; 68 for(int j=0;j<=ext;j++) tmp[j]=(f[st][j]-g[v][j]+p)%p; 69 for(int j=ext;j;j--) tmp[j]=(1ll*tmp[j-1]*j%p+tmp[j])%p; 70 for(int j=0;j<=ext;j++) (f[v][j]+=tmp[j])%=p; 71 getans(v,st); 72 } 73 } 74 75 signed main(){ 76 n=read(); k=read(); init(); 77 for(int a,b,i=1;i<n;i++) 78 a=read(),b=read(), add(a,b); 79 dfs(1,0); getans(1,0); 80 for(int i=0;i<=k;i++) (ans+=1ll*s[k][i]*sum[i]%p)%=p; 81 ans=1ll*ans*inv2%p; 82 write(ans,'\n'); 83 return 0; 84 }
UPD:联赛后做到了原题。重构了一遍代码,更可读了。
exT3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL;
typedef double DB;
int read(){
int x=0,f=0; char ch=getchar();
while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char output[50];
void write(int x,char sp){
int len=0;
if(x<0) putchar('-'), x=-x;
do{ output[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}
void ckmax(int& x,int y){ x=x>y?x:y; }
void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=50010,KK=510,mod=10007;
int n,k,t,ans[NN],tmp[KK],s[KK][KK],f[NN][KK],g[NN][KK];
vector<int>e[NN];
void dfs(int u,int p){
for(int v:e[u]) if(v!=p){
dfs(v,u);
for(int i=0;i<=k;i++)
f[u][i]=(f[u][i]+g[v][i])%mod;
}
g[u][0]=++f[u][0];
for(int i=1;i<=k;i++)
g[u][i]=(i*f[u][i-1]+f[u][i])%mod;
}
void redfs(int u,int p){
for(int i=1;i<=k;i++)
ans[u]=(ans[u]+s[k][i]*f[u][i])%mod;
for(int v:e[u]) if(v!=p){
for(int i=0;i<=k;i++) tmp[i]=(mod+f[u][i]-g[v][i])%mod;
for(int i=k;i>=1;i--) tmp[i]=(tmp[i]+tmp[i-1]*i%mod)%mod;
for(int i=0;i<=k;i++) f[v][i]=(f[v][i]+tmp[i])%mod;
redfs(v,u);
}
}
signed main(){
s[0][0]=1;
for(int i=1;i<=500;i++)
for(int j=1;j<=500;j++)
s[i][j]=(s[i-1][j]*j+s[i-1][j-1])%mod;
t=read();
while(t--){
n=read(); k=read();
for(int u,v,i=1;i<n;i++){
u=read(); v=read();
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0); redfs(1,0);
for(int i=1;i<=n;i++) write(ans[i],'\n');
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++) e[i].clear();
}
return 0;
}
T4 树
暴力分层动态开点线段树:
$code:$
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 namespace IO{ 5 inline int read(){ 6 char ch=getchar(); int x=0,f=1; 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<<4)+(1<<5); x/=10; }while(x); 15 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 16 } 17 inline int max(int x,int y){ return x<y?y:x; } 18 inline int min(int x,int y){ return x<y?x:y; } 19 inline void swap(int &x,int &y){ x^=y^=x^=y; } 20 inline void chmax(int &x,int y){ x=x<y?y:x; } 21 inline void chmin(int &x,int y){ x=x<y?x:y; } 22 } using namespace IO; 23 24 const int NN=3e5+5; 25 int n,q,x,y,z,v,mx,op,cnt,idx,siz[NN],dfn[NN],to[NN<<1],nex[NN<<1],head[NN],w[NN],dep[NN]; 26 27 inline void add(int a,int b){ 28 to[++idx]=b; nex[idx]=head[a]; head[a]=idx; 29 to[++idx]=a; nex[idx]=head[b]; head[b]=idx; 30 } 31 32 void dfs(int s,int f){ 33 dfn[s]=++cnt; dep[s]=dep[f]+1; siz[s]=1; 34 chmax(mx,dep[s]); 35 for(int i=head[s];i;i=nex[i]) 36 if(to[i]!=f) dfs(to[i],s), siz[s]+=siz[to[i]]; 37 } 38 39 namespace segment_tree{ 40 int tot,root[NN],lc[NN*80],rc[NN*80],val[NN*80]; 41 void insert(int &rt,int l,int r,int opl,int opr,int vv){ 42 if(!rt) rt=++tot; 43 if(l>=opl&&r<=opr) return val[rt]+=vv,void(); 44 int mid=l+r>>1; 45 if(opl<=mid) insert(lc[rt],l,mid,opl,opr,vv); 46 if(opr>mid) insert(rc[rt],mid+1,r,opl,opr,vv); 47 } 48 int query(int rt,int l,int r,int pos,int ans){ 49 ans+=val[rt]; 50 if(l==r) return ans; 51 int mid=l+r>>1; 52 if(pos<=mid) return query(lc[rt],l,mid,pos,ans); 53 else return query(rc[rt],mid+1,r,pos,ans); 54 } 55 } using namespace segment_tree; 56 57 signed main(){ 58 n=read(); q=read(); 59 for(int a,b,i=1;i<n;i++) 60 a=read(),b=read(), add(a,b); 61 dfs(1,0); 62 while(q--){ 63 op=read(); 64 if(op==1){ 65 v=read(); x=read(); y=read(); z=read(); 66 int tmp=y+dep[v]; 67 while(tmp<=mx){ 68 insert(root[tmp],1,n,dfn[v],dfn[v]+siz[v]-1,z); 69 tmp+=x; 70 } 71 } 72 else{ 73 v=read(); 74 write(query(root[dep[v]],1,n,dfn[v],0),'\n'); 75 } 76 } 77 return 0; 78 }
正解离线分块,待补(不补