多校A层冲刺NOIP2024模拟赛05
T1、好数(number)
签到题
把选三个数相加拆为选择一个数,然后看前面有没有能用两个数组合出答案。
码(
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; #define mk make_pair #define ps push_back #define fi first #define se second const int N=1e6+10,inf=0x3f3f3f3f,B=2e5+10; const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7; inline ll read(){ char c=getchar_unlocked();ll x=0,f=1; while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked(); return x*f; } int n,a[N]; bool f[N]; signed main(){ // #ifndef ONLINE_JUDGE freopen("number.in","r",stdin); freopen("number.out","w",stdout); // #endif ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); n=read(); for(int i=1;i<=n;++i) a[i]=read(); int ans=0; for(int i=1;i<=n;++i){ for(int j=1;j<i;++j){ if(f[a[i]-a[j]+B]){++ans;break;} } for(int j=1;j<=i;++j) f[a[i]+a[j]+B]=1; } cout<<ans; }
T2、SOS字符串(sos)
也算签到题吧,但我被评测机创飞了。
直接暴力枚举写了个狗屎DP,算了下时间来到了 4e8。。。但加了一点剪枝后在本地跑了0.64s,然后自信交卷了。然后就T了!
所以评测机一秒到底能跑多少啊。
说下改进后的DP
一维状态记已经有了多少个
这个确实快,1e8本地只跑了0.94s。
码(
// ubsan: undefined // accoders #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; #define mk make_pair #define ps push_back #define fi first #define se second const int N=1e6+10,inf=0x3f3f3f3f; const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7; inline ll read(){ char c=getchar_unlocked();ll x=0,f=1; while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked(); return x*f; } #define mo(x) (x>=mod?x-=mod:0) int n; ll f[2][4][4]; signed main(){ freopen("sos.in","r",stdin); freopen("sos.out","w",stdout); // int ti=clock(); ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); n=read(); f[0][3][0]=1; int now=0,to=1; for(int i=0;i<n;++i){ for(int k=0;k<=2;++k){ (f[to][3][k]+=((f[now][0][k]+f[now][1][k]+f[now][2][k]+f[now][3][k])*25ll-f[now][0][k]))%=mod; (f[to][1][k]+=f[now][0][k]);f[to][1][k]>=mod?f[to][1][k]-=mod:0; (f[to][0][k]+=(f[now][3][k]+f[now][2][k]+f[now][0][k]))%=mod; (f[to][2][k+1]+=f[now][1][k]);f[to][2][k+1]>=mod?f[to][2][k+1]-=mod:0; f[now][0][k]=f[now][1][k]=f[now][2][k]=f[now][3][k]=0; } (f[to][2][3]+=f[now][2][3]*26ll)%=mod;f[now][2][3]=0; swap(now,to); } cout<<f[now][2][3]<<'\n'; // cout<<(clock()-ti)/1e6<<endl; }
T3、集训营的气球(balloon)
退背包的板子吧。
第一次接触退背包的题,但是考场上竟然YY出来了。
首先背包肯定会跑,但是每次修改都要重新跑一边背包的话复杂度就太高了。
但是你可以观察到每次只会修改一个地方,然后全局询问,那么你就可以用线段树去维护这个背包,直接单点修改即可,非常的好写。时间复杂度是 O
线段树
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; #define mk make_pair #define ps push_back #define fi first #define se second const int N=1e6+10,inf=0x3f3f3f3f; const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7; inline ll read(){ char c=getchar_unlocked();ll x=0,f=1; while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked(); return x*f; } int f[N<<1][21],a[N],b[N],n,C; #define mo(x) (x>=mod?x-=mod:0) inline void up(int a[],int b[],int c[]){ for(int i=0;i<=C;++i) a[i]=0; for(int i=0;i<=C;++i){ for(int j=0;j<=C;++j) (a[i+j>C?C:i+j]+=(ll)b[i]*c[j]%mod)%=mod; } } inline void add(int k,int l,int r,int pos){ if(l==r)return (void)(f[k][0]=b[l],f[k][1]=a[l]); int mid=l+r>>1; pos<=mid?add(k<<1,l,mid,pos):add(k<<1|1,mid+1,r,pos); for(int i=0;i<=C;++i) f[k][i]=0; for(int i=0;i<=C;++i){ for(int j=0;j<=C;++j) f[k][i+j>C?C:i+j]+=(ll)f[k<<1][i]*f[k<<1|1][j]%mod,mo(f[k][i+j>C?C:i+j]); } } inline void jian(int k,int l,int r){ if(l==r)return (void)(f[k][0]=b[l],f[k][1]=a[l]); int mid=l+r>>1; jian(k<<1,l,mid),jian(k<<1|1,mid+1,r); for(int i=0;i<=C;++i){ for(int j=0;j<=C;++j) f[k][i+j>C?C:i+j]+=(ll)f[k<<1][i]*f[k<<1|1][j]%mod,mo(f[k][i+j>C?C:i+j]); } } signed main(){ // #ifndef ONLINE_JUDGE freopen("balloon.in","r",stdin); freopen("balloon.out","w",stdout); // #endif // double ti=clock(); // cout<<sizeof(f)/1024/1024<<endl; ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); n=read();C=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=n;++i) b[i]=read(); jian(1,1,n); int m=read(); for(int j=1,pos;j<=m;++j){ pos=read();a[pos]=read(),b[pos]=read(); add(1,1,n,pos); cout<<f[1][C]<<'\n'; } // cout<<(double)(clock()-ti)/1e6<<'\n'; }
其实但是打完这个就去看T4了,没想着能A了,但后面那个T4属实不太会,于是又回来看T3。
看看我们能不能把修改的复杂度降下来。先想想为什么线段树比暴力快呢?或者说为什么暴力很慢呢?因为每次重新跑一遍DP会丢失很多之前已经统计好的信息,然后又白白的去统计一堆已经统计过的信息,但其实有用的信息只有修改的地方改变了,那么线段树之所以快,就是因为他重新统计的信息少,浪费的信息少。那么我们能不能不浪费任何信息呢?
其实是可以的,背包与物品的顺序是无关的,那么我们可以假设每次修改的位置都是最后一个物品,那么第 n-1 个及以前的物品是不变的,再跑一次DP时跑到这的DP数组也是一点不变的,但我们又不能每次记倒数第二个DP数组,因为他并不是真的只改最后一个,只是假设而已,那么我们就得设法通过最终状态的DP数组求出 去除修改物品后的DP数组。
这个就叫退背包,我们通过正向的转移柿子去逆向解出原来的DP数组。
那这个题举例子吧:
设
我们可以写出转移柿子
那么我们把pos当做最后一个位置,于是就可以把柿子倒回去。设
那么直接把上面的等式右边的
怎么解呢,高斯消元?
其实不用,我们要知道
求出
至此,就是场上想的退背包的过程,要学习退背包可以去专门学学,有板子题:P4141 消失之物。
点击查看代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; #define mk make_pair #define ps push_back #define fi first #define se second const int N=1e6+10,inf=0x3f3f3f3f; const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7; #define int ll inline ll read(){ char c=getchar_unlocked();ll x=0,f=1; while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked(); return x*f; } ll f[2][22],a[N],b[N],ff[22],C,n; inline ll qpow(ll x,ll y){ ll ans=1;x%=mod; while(y){ if(y&1)ans=ans*x%mod; x=x*x%mod;y>>=1; } return ans; } signed main(){ // #ifndef ONLINE_JUDGE freopen("balloon.in","r",stdin); freopen("balloon.out","w",stdout); // #endif ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); n=read(),C=read(); ll sumb=1; for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=n;++i) b[i]=read(),sumb=sumb*b[i]%mod; int now=1,fr=0; f[0][0]=1; for(int i=1;i<=n;++i){ for(int j=0;j<=C+1;++j){ // f[now][j]=0; f[now][j]=f[fr][j]*b[i]%mod; if(j)f[now][j]+=f[fr][j-1]*a[i]%mod; if(j==C+1)f[now][j]+=f[fr][j]*a[i]%mod; f[now][j]%=mod; } swap(now,fr); } for(int i=0;i<=C+1;++i) f[0][i]=f[fr][i]; int m=read(); for(int i=1,pos;i<=m;++i){ pos=read(); ll nyb=qpow(b[pos],mod-2); ff[0]=sumb*nyb%mod; for(int j=1;j<=C;++j){ ff[j]=((f[0][j]-ff[j-1]*a[pos]%mod)*nyb%mod+mod)%mod; } ff[C+1]=((f[0][C+1]-ff[C]*a[pos]%mod)*qpow(a[pos]+b[pos],mod-2)%mod+mod)%mod; a[pos]=read(),b[pos]=read();sumb=sumb*nyb%mod*b[pos]%mod; for(int j=0;j<=C+1;++j){ f[0][j]=ff[j]*b[pos]%mod; if(j)f[0][j]+=ff[j-1]*a[pos]%mod; if(j==C+1)f[0][j]+=ff[j]*a[pos]%mod; f[0][j]%=mod; } cout<<(f[0][C]+f[0][C+1])%mod<<'\n'; } }
T4、连通子树与树的重心(tree)
先膜拜一下Qyun,全场唯一一个拿分而且拿了50分的。
其实当时看到这个题的时候,有点抵触,因为这种题其实我没什么头绪,整了半个小时也没整出来啥,就放弃了。
这道题主要就是用重心的性质。
不太想写了,挂个题解吧。
咕了
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; #define mk make_pair #define ps push_back #define fi first #define se second const int N=1e6+10,inf=0x3f3f3f3f; const ll linf=0x3f3f3f3f3f3f3f3f,mod=10007; inline ll read(){ char c=getchar_unlocked();ll x=0,f=1; while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked(); return x*f; } int n,cnt,hd[N],rt,sz[N],man[N],rt2; int f[5001][5001],g[5001]; struct jj{ int to,next; }bi[N<<1]; inline void add(int x,int y){bi[++cnt]={y,hd[x]},hd[x]=cnt,bi[++cnt]={x,hd[y]},hd[y]=cnt;} inline void findrt(int x,int f){ sz[x]=1,man[x]=0; for(int i=hd[x];i;i=bi[i].next){int j=bi[i].to;if(j!=f)findrt(j,x),sz[x]+=sz[j],man[x]=max(man[x],sz[j]);} man[x]=max(man[x],n-sz[x]); if(man[x]<=(n>>1))rt=x; } inline void dfs(int x,int ff){ f[x][1]=1;sz[x]=1; for(int i=hd[x];i;i=bi[i].next){ int j=bi[i].to; if(j!=ff){ dfs(j,x); for(int p=sz[x];p;--p){ for(int k=1;k<=sz[j];++k) (f[x][p+k]+=f[x][p]*f[j][k]%mod)%=mod; } sz[x]+=sz[j]; } } } signed main(){ // #ifndef ONLINE_JUDGE freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); // #endif ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); n=read(); for(int i=1;i<n;++i) add(read(),read()); rt=1; findrt(1,0); for(int i=hd[rt];i;i=bi[i].next){ int j=bi[i].to; if(man[j]==man[rt])rt2=j; } if(rt2){ dfs(rt,rt2),dfs(rt2,rt); int ans=0; for(int i=1;i<=n;++i) (ans+=f[rt][i]*f[rt2][i]%mod)%=mod; cout<<ans<<'\n'; return 0; } else{ dfs(rt,0); //tuibao int ans=0; for(int i=1;i<=n;++i) ans=(ans+f[rt][i])%mod; for(int i=hd[rt];i;i=bi[i].next){ int j=bi[i].to; for(int k=1;k<=n;++k){ g[k]=f[rt][k]; for(int p=1;p<=min(k,sz[j]);++p){ g[k]=(g[k]-g[k-p]*f[j][p]%mod+mod)%mod; } for(int p=(k+1)>>1;p<=sz[j]&&p<=k;++p) ans=(ans-g[k-p]*f[j][p]%mod+mod)%mod; } } cout<<ans<<'\n'; } }
hard to say
今天晚上开了动员大会,怎么说呢。算是点醒我吗? 今天看见暑假集训模拟赛的结果,退了不少,一开始还是在前面的,但是后面几场几乎是垫底了。 因为点什么呢,大概是当时有点拒绝思考吧,可能就确实是怕输吧。 但怕有什么用呢,就好好打模拟赛吧,也不要沾沾自喜,也不要患得患失,就学吧。 那我后悔学信奥吗,其实总来没后悔过,并不是因为今天huge说的话,而只是因为,就确实不后悔,其实平常总开玩笑,“还好我是信奥的”,但一点不后悔。放下架子,往前走吧。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)