2025省选模拟13
2025省选模拟13
P1025. Easy Problem
-
部分分
- 设
表示 时 对答案的贡献,状态转移方程为 ,其中 表示 的次大值。 - 设
,此时 可能会凹凸不平,不妨对 取前缀 后只维护斜率变化点处的转移。- 这个优化在随机数据下表现良好。
- 设
点击查看代码
ll a[6010],ans[6010],f[6010][6010],g[6010][6010]; vector<ll>opt; ll median(ll a,ll b,ll c) { return a+b+c-max({a,b,c})-min({a,b,c}); } int main() { #define Isaac #ifdef Isaac freopen("easy.in","r",stdin); freopen("easy.out","w",stdout); #endif ll n,maxx=0,i,j,k; cin>>n; for(i=1;i<=3*n;i++) cin>>a[i]; for(i=1;i<=3*n;i++) { g[i+2][i]=median(a[i],a[i+1],a[i+2]); maxx=max({a[i],a[i+1],a[i+2]}); for(j=i+3;j<=3*n;j++) { if(a[j]>=maxx) { g[j][i]=maxx; maxx=a[j]; } else g[j][i]=max(g[j-1][i],a[j]); } } memset(f,-0x3f,sizeof(f)); for(i=0;i<=n;i++) f[0][i]=0; opt.push_back(0); for(j=1;j<=n;j++) { for(i=3*j;i<=3*n;i++) { for(k=0;k<opt.size()&&opt[k]<=i-3;k++) f[j][i]=max(f[j][i],f[j-1][opt[k]]+g[i][opt[k]+1]); ans[j]=max(ans[j],f[j][i]); } opt.clear(); opt.push_back(3*j); for(i=3*j+1;i<=3*n;i++) { if(f[j][i]>f[j][i-1]) opt.push_back(i); else f[j][i]=f[j][i-1]; } } for(i=1;i<=n;i++) cout<<ans[i]<<" "; return 0; }
-
正解
- 进一步挖掘对
取前缀 后的其他一些性质。此时能够直接从 作为中位数的极短区间左端点转移而来,考虑单调栈预处理。 - 当选出的
和 相邻时会有一些 Corner Case ,添加相邻 个位置的转移即可。
点击查看代码
ll a[6010],l[6010],r[6010],f[6010][6010]; stack<ll>s; vector<ll>opt[6010]; ll median(ll a,ll b,ll c) { return a+b+c-max({a,b,c})-min({a,b,c}); } int main() { #define Isaac #ifdef Isaac freopen("easy.in","r",stdin); freopen("easy.out","w",stdout); #endif ll n,i,j,k; cin>>n; for(i=1;i<=3*n;i++) { cin>>a[i]; while(s.empty()==0&&a[s.top()]<=a[i]) { if(a[s.top()]==a[i]) l[i]=s.top(); s.pop(); } if(l[i]==0&&s.empty()==0) l[i]=s.top(); s.push(i); } while(s.empty()==0) s.pop(); for(i=3*n;i>=1;i--) { while(s.empty()==0&&a[s.top()]<=a[i]) { if(a[s.top()]==a[i]) r[i]=s.top(); s.pop(); } if(r[i]==0&&s.empty()==0) r[i]=s.top(); opt[r[i]].push_back(i); s.push(i); } memset(f,-0x3f,sizeof(f)); f[0][0]=0; for(i=1;i<=3*n;i++) { if(l[i]!=0&&l[i]<i-1) for(j=1;3*j<=i;j++) f[i][j]=max(f[i][j],f[l[i]-1][j-1]+a[i]); for(k=0;k<opt[i].size();k++) if(opt[i][k]+1<i) for(j=1;3*j<=i;j++) f[i][j]=max(f[i][j],f[opt[i][k]-1][j-1]+a[opt[i][k]]); if(i-3>=0) for(j=1;3*j<=i;j++) f[i][j]=max(f[i][j],f[i-3][j-1]+median(a[i-2],a[i-1],a[i])); for(j=0;3*j<=i;j++) f[i][j]=max(f[i][j],f[i-1][j]); } for(i=1;i<=n;i++) cout<<f[3*n][i]<<" "; return 0; }
- 进一步挖掘对
P1026. Trash Problem
-
部分分
- 部分分
- 枚举子矩形后
进行 。子矩形合法当且仅当 水平、竖直扩展的极长区间长度为偶数,且存在一种合法的覆盖方案。 - 覆盖时考虑从左到右、从上到下枚举每个没有被覆盖的
,分别向四个方向进行扩展,一旦发现无法扩展说明不存在合法方案。- 如果不进行判断时挂一组经典
。0110 1111 1111 0110
- 如果不进行判断时挂一组经典
- 正确性好像不是很显然。
- 枚举子矩形后
点击查看代码
int up[310][310],dw[310][310],st[310][310],ed[310][310],sum[310][310],vis[310][310]; char s[310][310]; bool check(int x1,int y1,int x2,int y2) { if((sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1])%4!=0) return false; for(int i=x1;i<=x2;i++) { for(int j=y1;j<=y2;j++) { vis[i][j]=0; if(s[i][j]=='1'&&((min(ed[i][j],y2)-max(st[i][j],y1))%2==0 ||(min(dw[i][j],x2)-max(up[i][j],x1))%2==0)) return false; } } for(int i=x1;i<=x2;i++) { for(int j=y1;j<=y2;j++) { if(s[i][j]=='1'&&vis[i][j]==0) { if(i-1>=x1&&j-1>=y1&&s[i-1][j-1]=='1'&&vis[i-1][j-1]==0 &&s[i][j-1]=='1'&&vis[i][j-1]==0&&s[i-1][j]=='1'&&vis[i-1][j]==0) { vis[i-1][j-1]=vis[i][j-1]=vis[i-1][j]=vis[i][j]=1; continue; } if(i+1<=x2&&j-1>=y1&&s[i+1][j-1]=='1'&&vis[i+1][j-1]==0 &&s[i][j-1]=='1'&&vis[i][j-1]==0&&s[i+1][j]=='1'&&vis[i+1][j]==0) { vis[i+1][j-1]=vis[i][j-1]=vis[i+1][j]=vis[i][j]=1; continue; } if(i-1>=x1&&j+1<=y2&&s[i-1][j+1]=='1'&&vis[i-1][j+1]==0 &&s[i][j+1]=='1'&&vis[i][j+1]==0&&s[i-1][j]=='1'&&vis[i-1][j]==0) { vis[i-1][j+1]=vis[i][j+1]=vis[i-1][j]=vis[i][j]=1; continue; } if(i+1<=x2&&j+1<=y2&&s[i+1][j+1]=='1'&&vis[i+1][j+1]==0 &&s[i][j+1]=='1'&&vis[i][j+1]==0&&s[i+1][j]=='1'&&vis[i+1][j]==0) { vis[i+1][j+1]=vis[i][j+1]=vis[i+1][j]=vis[i][j]=1; continue; } return false; } } } return true; } int main() { #define Isaac #ifdef Isaac freopen("trash.in","r",stdin); freopen("trash.out","w",stdout); #endif int n,ans=0,i,j,x1,y1,x2,y2; cin>>n; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { cin>>s[i][j]; sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(s[i][j]=='1'); up[i][j]=(s[i-1][j]=='1')?up[i-1][j]:i; st[i][j]=(s[i][j-1]=='1')?st[i][j-1]:j; } } for(i=n;i>=1;i--) { for(j=n;j>=1;j--) { dw[i][j]=(s[i+1][j]=='1')?dw[i+1][j]:i; ed[i][j]=(s[i][j+1]=='1')?ed[i][j+1]:j; } } for(x1=1;x1<=n;x1++) for(y1=1;y1<=n;y1++) for(x2=x1;x2<=n;x2++) for(y2=y1;y2<=n;y2++) if(check(x1,y1,x2,y2)==true) ans++; cout<<ans<<endl; return 0; }
- 部分分
-
正解
- 考虑只枚举左上边界,然后从上往下进行扩展,记录每个格子当前是
的一半还是已经完成。每次扩展的时候如果是一半且下一个是 ,则包含这一列的都不合法,否则将状态 。某个时刻合法当且仅当所有 的状态都是已经完成,且不包含前面提到的那些不合法的列且出现过的所有连续状态 的区间交的长度为偶数。 - 前两个条件在扩展的时候不断向左移动右端点是容易处理的。需要水平方向预处理出能扩展到的最远位置。
- 对于第三个限制条件,我们实际上可以只考虑出现在某个奇数个数位置的
的影响,预处理其他 位置选或不选的贡献,使用bitset
加速即可,时间复杂度为 。
点击查看代码
int r[310][310]; char s[310][310]; bitset<310>st[310][310],zero[310][310],a,b,c,f[310]; int solve(int x,int y,int n) { int sum=0; for(int i=y;i<=n;i++) { if(s[x][i]=='1') { sum++; if(sum%2==1) st[x][y][i-y]=1; } else if(sum%2==1) return i-2; else sum=0; } return (sum%2==1)?n-1:n; } int main() { #define Isaac #ifdef Isaac freopen("trash.in","r",stdin); freopen("trash.out","w",stdout); #endif int n,ans=0,limit,len,i,j,k; cin>>n; f[0][0]=1; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) cin>>s[i][j]; for(j=1;j<=n;j++) r[i][j]=solve(i,j,n); f[i]=f[i-1]; f[i][i]=1; } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { a=b=st[i][j]; limit=r[i][j]-j+1; len=min((int)a._Find_first(),limit); if(len-1>=0) ans+=len-(b&f[len-1]).count();// 部分 1 无法自己独立选择 for(k=i+1;k<=n;k++) { limit=min(limit,r[k][j]-j+1);// 向左移动右端点 c=a; b|=(a&st[k][j]); a^=st[k][j];// 翻转状态 c&=a; limit=min(limit,(int)c._Find_first());// 翻转后可能会导致不合法,需要向左移动右端点 len=min((int)a._Find_first(),limit); if(len-1>=0) ans+=len-(b&f[len-1]).count(); } } } cout<<ans<<endl; return 0; }
- 考虑只枚举左上边界,然后从上往下进行扩展,记录每个格子当前是
P1027. Random Problem
-
部分分
:模拟。
点击查看代码
const int p=998244353; bitset<90010>a[90010]; int main() { #define Isaac #ifdef Isaac freopen("random.in","r",stdin); freopen("random.out","w",stdout); #endif int n,m,x1,y1,x2,y2,ans=0,i,j,k; scanf("%d%d",&n,&m); for(k=1;k<=m;k++) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); swap(x1,y1); swap(x2,y2); if(x1>x2||y1>y2) { swap(x1,x2); swap(y1,y2); } if(x2-x1==y2-y1) for(i=x1,j=y1;i<=x2;i++,j++) a[i][j]=1; else if(x1==x2) for(i=y1;i<=y2;i++) a[x1][i]=1; else for(i=x1;i<=x2;i++) a[i][y1]=1; } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) if(a[i][j]==1) ans=(ans+1ll*i*j%p)%p; } printf("%d\n",ans); return 0; }
-
正解
总结
一开始以为有决策单调性或可以进行线段树优化,然后发现假了。 口胡出第一部分的 后发现死活过不了 的数据,被迫花一个多小时对每个合法子矩形进行手动 。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18730245,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2024-02-22 2024初三集训模拟测试4
2024-02-22 2024初三集训模拟测试3