AtCoder Grand Contest 026
链接
D. Histogram Coloring
考虑如果有 这样的矩形,那么这个矩形往上往下都是固定的。如果有 这样的矩形,那么往左往右都是固定的。
假如是一个矩形,那么对于相邻两列,如果存在 ,无论这两行其他位置怎么填,其他列的位置就固定。换句话说枚举第一列有没有连续的字符,如果有那么后面唯一,否则后面有两种情况。
如果不是矩形,考虑分别每次最下方的大矩形。如果:上面的小矩形没有连续的字符,那么下面的字符同上面。否则无论第一行怎么填,只有每块的开头可以任意填。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 110
#define mod 1000000007
using namespace std;
int ksm(int a,int b=mod-2)
{
int r=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) r=1ll*r*a%mod;
return r;
}
int h[N],n;
int f[N*2][2],tt;
int solve(int l,int r,int lim)
{
if(l>r) return 0;
int mn=*min_element(h+l,h+r+1),u=++tt;
int p=l,c=0;f[u][0]=f[u][1]=1;
for(int i=l;i<=r+1;i++)
if(h[i]==mn || i>r)
{
int v=solve(p,i-1,mn);
f[u][0]=1ll*f[u][0]*f[v][0]%mod,f[u][1]=1ll*f[u][1]*(f[v][0]+f[v][1])%mod;
p=i+1;++c;
}
f[u][1]=(1ll*f[u][0]*(ksm(2,mn-lim)-2)%mod+1ll*f[u][1]*ksm(2,c-1)%mod+mod)%mod;
f[u][0]=1ll*ksm(2,mn-lim)*f[u][0]%mod;
return u;
}
int main()
{
scanf("%d",&n);f[0][0]=1;
for(int i=1;i<=n;i++) scanf("%d",&h[i]);
int u=solve(1,n,0);
printf("%d\n",f[u][1]);
return 0;
}
E. Synchronized Subsequence
考虑后 对 的情况,如果是 ,那么这其中的所有部分一定删掉,令 表示向后第一个完全不在其中的 ,有 。
否则是 的形式,考虑如果有 ,那么下一个 匹配的 同样要保留,这样保留的实际是一段区间,最后同样找到向后的第一个完全不在其中的 ,有 。
由于 很小,直接比较字符串即可。复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 6010
using namespace std;
char s[N];
string f[N];
int r[N],a[N],b[N],id[N];
int main()
{
int n;
scanf("%d%s",&n,s+1);n*=2;
int m=0;
for(int i=1;i<=n;i++) if(s[i]=='a') a[++m]=i,id[i]=m;
m=0;for(int i=1;i<=n;i++) if(s[i]=='b') b[++m]=i,id[i]=m;
for(int i=1;i<=m;i++) r[a[i]]=b[i],r[b[i]]=a[i];
r[n+1]=n+1;
f[m]=a[m]<b[m]?"ab":"ba";
// cout<<f[m]<<endl;
for(int i=m-1;i>=1;i--)
{
f[i]=f[i+1];
if(a[i]<b[i])
{
for(int j=b[i]+1;j<=n+1;j++)
if(r[j]>b[i]){f[i]=max(f[i],"ab"+f[id[j]]);break;}
}
else
{
int mx=a[i];
for(int j=b[i]+1;j<=mx;j++) if(s[j]=='b') mx=max(mx,r[j]);
string t;
for(int j=b[i];j<=mx;j++) if(r[j]>=b[i]) t+=s[j];
for(int j=mx+1;j<=n+1;j++)
if(r[j]>mx){f[i]=max(f[i],t+f[id[j]]);break;}
}
}
cout<<f[1]<<endl;
return 0;
}
F. Manju Game
题意
有一排卡片,第 张卡片价值 。双方轮流进行一下操作:
- 如果当前第一次操作或者上一张操作的卡片没有卡片与其相邻,则可以从剩余卡片中随意拿一张。
- 否则只能拿与上一张卡片相邻的一张卡片。
- 没有卡片时结束。
问先手最多能拿多少价值的卡片。
题解
分类讨论。
如果张数是偶数,那么获得先手权的人最优方案一定不劣于选奇数的卡片或选偶数的卡片的较大值(显然取第一张或最后一张后结果就确定了)。否则考虑后手可以选择剩下是偶数的一边,这样获得先手权后最坏也可以直接钦定选第一张,所以先手结果不优于直接取。
所以张数是偶数时就是奇数卡片与偶数卡片的较大值。
如果是奇数,首先先手可以选第一张选走所有奇数的卡片。
否则考虑一定会选标号为偶数的卡片,因为这样先手权仍然在自己手上。考虑这种情况下相当于用若干偶数的位置划分开整个序列,要求相邻区间的较小值最大,这样无论后手选哪一个方向,都能保证取到较小值。
直接二分这个最小值。复杂度 。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define N 300010
using namespace std;
int a[N],s[N],n;
bool check(int x)
{
int res=0;
for(int i=1;i<=n;i+=2) if(res+x<=s[i]) res=min(res,s[i+1]);
return x+res<=s[n];
}
int c[2];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[i&1]+=a[i];
if(n%2==0){printf("%d %d\n",max(c[0],c[1]),min(c[0],c[1]));return 0;}
for(int i=1;i<=n;i++) s[i]=s[i-1]+(i&1?1:-1)*a[i];
int l=0,r=c[0]+c[1],ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d %d\n",c[0]+ans,c[1]-ans);
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/agc026.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理