noip模拟16
A 草莓
真是唐题。原本以为是 dp,没想到是贪心。
然后搓了个 的 dp 以为正确性假假的,就优化成了 。。。
dp 很简单,首先排序,设 表示横着掰到第 个,竖着拜到第 个的最小答案,然后从 和 分别更新即可。
其实这就是贪心的思路,因为你的 dp 过程无非就是对于两个序列顺序一定时, 和 的选取更优策略。那直接把 和 塞到一起排个序就好了。
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long const int M=5e3+3,N=2e5+3; int x[N],y[N]; int dp[M][M]; int n,m; int ans=1e18; inline bool cmp(int a,int b) { return a>b; } int sumx[N],sumy[N]; signed main() { freopen("guiltiness.in","r",stdin); freopen("guiltiness.out","w",stdout); cin>>n>>m; bool _1=1,_2=1,_=1; int cnt=0; for(int i=2;i<=n;i++) cin>>x[i]; for(int i=2;i<=m;i++) cin>>y[i]; ans=0; sort(x+2,x+n+1),sort(y+2,y+m+1); for(int i=n,j=m;i>1||j>1;) { if(x[i]>y[j]) ans+=(m-j+1)*x[i],i--; else ans+=(n-i+1)*y[j],j--; } cout<<ans; return 0; }
B 三色
还是神秘 dp。弱化版是紫色。
考虑一个 的 dp,设 表示现在在 ,即最前面的颜色在 ,第二个颜色在 ,第三个颜色在 的方案数。则通过刷表,可以从 分别转移到:
-
表示 放第一种颜色;
-
表示 放第二种颜色;
-
表示 放第三种颜色。
然后因为我们有 个限制,那么就需要对每次转移枚举的 进行限制。
点击查看代码
#include<bits/stdc++.h> using namespace std; int T; const int N=504,mod=1e9+7; int ***dp,*mi1,*mx3,*mi2,*mx2; inline bool ck(int i,int j,int k) { return j<mi1[i]&&k<mi2[i]&&j>=mx2[i]&&k>=mx3[i]; } namespace q{ int n,m; void work() { cin>>n>>m; dp=new int **[n+1]; mi1=new int [n+2],mx3=new int [n+2],mi2=new int [n+2],mx2=new int [n+3]; for(int i=0;i<=n;i++) { dp[i]=new int *[n+1]; for(int j=0;j<=n;j++) { dp[i][j]=new int [n+1]; for(int k=0;k<=n;k++) dp[i][j][k]=0; } } for(int i=0;i<=n;i++)mi1[i]=mi2[i]=0x3f3f3f3f,mx3[i]=mx2[i]=0; while(m--) { int l,r,x;cin>>l>>r>>x; if(x==1) mi1[r]=min(mi1[r],l); if(x==2) mi2[r]=min(mi2[r],l),mx2[r]=max(mx2[r],l); if(x==3) mx3[r]=max(mx3[r],l); } dp[0][0][0]=1; for(int i=0;i<n;i++) { for(int j=0;j<max(1,i);j++) { for(int k=0;k<max(1,j);k++) { if(ck(i,j,k)) { // cout<<i<<" "<<j<<" "<<k<<"\n"; dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod; dp[i+1][i][k]=(dp[i+1][i][k]+dp[i][j][k])%mod; dp[i+1][i][j]=(dp[i+1][i][j]+dp[i][j][k])%mod; } } } } int ans=0; for(int i=0;i<n;i++) { for(int j=0;j<max(i,1);j++) { if(ck(n,i,j))ans=(ans+dp[n][i][j])%mod; } } cout<<ans<<"\n"; delete [] mi1;delete []mx3;delete []mi2;delete []mx2;delete []dp; } } signed main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; while(T--) q::work(); return 0; }
然后考虑优化。把每一层的 看作一个平面,那么每次转移都是从上一次的一个点,或者一条线的状态转移的。
然后就每次更新这些地方就行。具体可见 这篇博客。
点击查看代码
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; int T,n,m; namespace q{ int *lsum,*csum,*minj,*maxj,*mink,*maxk,**f; int *t,lj,*rk,*lk; inline int sub(int x,int y) { return ((x-y)%mod+mod)%mod; } inline void del(int j,int k) { lsum[j]=sub(lsum[j],f[j][k]); csum[k]=sub(csum[k],f[j][k]); f[j][k]=0; } void memst(int i) { for(int j=lj;j<i;j++) { if(j<minj[i]||j>maxj[i]) { for(int k=0;k<=n;k++) del(j,k); lk[j]=n,rk[j]=0; } else { for(int k=lk[j];k<mink[i];k++) del(j,k); for(int k=rk[j];k>maxk[i];k--) del(j,k); lk[j]=max(lk[j],mink[i]); rk[j]=min(rk[j],maxk[i]); } } lj=max(lj,minj[i]); } void work() { cin>>n>>m; lsum=new int [n+1],csum=new int [n+1],minj=new int [n+1],maxj=new int [n+1], mink=new int [n+1],maxk=new int [n+1],t=new int [n+1],lk=new int [n+1],rk=new int [n+1]; f=new int *[n+1]; for(int i=0;i<=n;i++) { f[i]=new int [n+1]; for(int j=0;j<=n;j++) f[i][j]=0; lsum[i]=csum[i]=minj[i]=mink[i]=0; maxj[i]=maxk[i]=n; lk[i]=0,rk[i]=n; } int l,r,x; while(m--) { cin>>l>>r>>x; if(x==1) maxj[r]=min(maxj[r],l-1); if(x==2) minj[r]=max(minj[r],l),maxk[r]=min(maxk[r],l-1); if(x==3) minj[r]=max(minj[r],l+1),mink[r]=max(mink[r],l); } lj=0; f[0][0]=lsum[0]=csum[0]=1; for(int i=1;i<=n;i++) { if((double)clock()/CLOCKS_PER_SEC>=0.96) return cout<<0,void(); for(int k=0;k<i;k++) t[k]=(lsum[k]+csum[k])%mod; if(minj[i]<=i-1&&maxj[i]>=i-1) { for(int k=mink[i];k<=min(maxk[i],max(0,i-2));k++) { f[i-1][k]=(f[i-1][k]+t[k])%mod; csum[k]=(csum[k]+t[k])%mod; lsum[i-1]=(lsum[i-1]+t[k])%mod; } } memst(i); } int ans=0; for(int i=0;i<=n;i++) ans=(ans+lsum[i])%mod; cout<<ans<<"\n"; delete []f,delete []lsum,delete []csum,delete []minj,delete []mink,delete []maxj,delete []maxk,delete []lk,delete []rk,delete []t; } } signed main() { freopen("color.in","r",stdin); freopen("color.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; while(T--)q::work(); return 0; }
C 博弈
根据题意,这三个数一定是向着减小差距的方向修改的。
考虑最简单的、对于一个三元组的 check。你会发现如果他们三个都不同的话,一定能赢。否则,如果三个数相等,那第一把就输了。那么 互不相同的方案数就是 。
看来剩下的只剩下形如 这种形式的了。还是考虑多少次能够到达修改的临界点,发现当 的位数是偶数个才能满足。
双 :对每一位开一个 map,对于所有 枚举 ;
单 :建一棵从下往上的 Trie 树。目前正在卡常。
D 后缀数组
分做法:
首先用 FHQ 维护那 个修改操作,时间复杂度 。实现是简单的,具体可以参考文艺平衡树。
然后,统计所有 的数量,答案是 。
点击查看代码
#include<bits/stdc++.h> using namespace std; int n,m; const int N=1e5+4,mod=998244353; int a[N]; int acnt; struct TREE { struct NODE { int l,r,val,siz,key; bool lz; }tree[N<<1]; int cnt{},root{}; inline int newnode(int val) { tree[++cnt].val = val,tree[cnt].siz = 1,tree[cnt].key = rand(); return cnt; } inline void ins(int val){root = merge(root,newnode(val));} inline void pushup(int x){tree[x].siz = tree[tree[x].l].siz + tree[tree[x].r].siz + 1;} inline void pushdown(int x) { if(tree[x].lz) { tree[x].l ^= tree[x].r ^= tree[x].l ^= tree[x].r; tree[tree[x].l].lz ^= 1;tree[tree[x].r].lz ^= 1; tree[x].lz = false; } } inline void split(int x,int siz, int &l,int &r) { if(!x) l = r = 0; else { pushdown(x); if(tree[tree[x].l].siz < siz){ l = x;split(tree[x].r,siz-tree[tree[x].l].siz-1,tree[x].r,r);} else{r = x;split(tree[x].l,siz,l,tree[x].l);} pushup(x); } } int x,y,z; inline int merge(int x,int y) { if(!x || !y) return x + y; if(tree[x].key > tree[y].key) { pushdown(x);tree[x].r = merge(tree[x].r,y); pushup(x);return x; } else { pushdown(y);tree[y].l = merge(x,tree[y].l); pushup(y);return y; } } inline void reverse(int l,int r) { split(root,l-1,x,y); split(y,r-l+1,y,z); tree[y].lz ^= 1; root = merge(merge(x,y),z); } inline void work(int l,int r) { split(root,l-1,x,y); split(y,r-l+1,y,z); root = merge(y,merge(x,z)); } inline void output(int x) { if(!x) return; pushdown(x); output(tree[x].l); a[++acnt] = tree[x].val; output(tree[x].r); } }tr; int rk[N],dp[N]; signed main() { freopen("sa.in","r",stdin); freopen("sa.out","w",stdout); srand(time(NULL)); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m; for(int i=1;i<=n;i++) tr.ins(i); while(m--) { int op,u,v;cin>>op>>u>>v; if(op==0) tr.work(u,v); else tr.reverse(u,v); } tr.output(tr.root); for(int i=1;i<=n;i++)rk[a[i]]=i; dp[1]=1; for(int i=2;i<=n;i++) { if(rk[a[i]+1]>rk[a[i-1]+1]) dp[i]=dp[i-1]*2%mod; else dp[i]=dp[i-1]; } cout<<dp[n]; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析