Codeforces Round #699 (Div. 2)
Posted C202044zxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #699 (Div. 2)相关的知识,希望对你有一定的参考价值。
E.Sorting Books
题目描述
解法
\\(\\tt Almost\\space art!The\\space art\\space of\\space enumeration!\\)
不难发现每本书最多移动一次,移动多次一定是不优的。
那么每本书就有两种状态:不移动和移动。我们枚举每本书的状态,如果以最后一本不动书为界限,那么前面的书如果属于同一种类,那么一定同时移动或者同时不移动,否则这本不动书就会使他们不能相聚。
所以我们枚举最后一本不动书,它后面的数一定要动,现在要决策它前面的书,其实就是选出来书种类的区间不能相交,那么设 \\(dp[i]\\) 表示前 \\(i\\) 本书最大的不动书个数,预处理出每种书的左端点 \\(l[i]\\),右端点 \\(r[i]\\) 和出现次数 \\(cnt[i]\\) 就可以简单转移了。
但是代码中你会发现名与实的分离,去看注释!
总结
当没有思路的时候,枚举法会有奇效,虽然我们不用他实现代码,但是枚举有助于思考。
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 500005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<\'0\' || c>\'9\') {if(c==\'-\') f=-1;}
while(c>=\'0\' && c<=\'9\') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,a[M],l[M],r[M],dp[M],cnt[M];
vector<pair<int,int>> g[M];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
if(!l[a[i]]) l[a[i]]=i;
r[a[i]]=i;
cnt[a[i]]++;
}
for(int i=1;i<=n;i++)
if(cnt[i])
g[l[i]-1].push_back(make_pair(r[i],cnt[i]));
for(int i=0;i<n;i++)
{
for(auto x:g[i])
dp[x.first]=max(dp[x.first],dp[i]+x.second);
dp[i+1]=max(dp[i+1],dp[i]);
}
int ans=n;
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=n;i>=1;i--)
{
cnt[a[i]]++;
ans=min(ans,n-dp[i-1]-cnt[a[i]]);
//后面的书不应该都动吗?为什么要减cnt[a[i]]
//因为dp[i-1]其实没有算清楚
//对于a[i]这个种类的书,可能会不动
//但是我们在前面考虑不到其不动的情况(看转移)
//所以实际上是考虑前面不动的情况
}
printf("%d\\n",ans);
}
F. AB Tree
题目描述
解法
显然的思路是按深度分层,最理想的状况是每一行的字符都一样,答案是深度。
如果不能达到这个状态呢?那么构造给出最优解是深度\\(+1\\)
我们按层数从小到大考虑,如果某一层不能填入相同的字符,设出现次数较大的字符为 \\(c\\),因为要降低对儿子的影响,所以把非叶节点填入颜色 \\(c\\),设 \\(m\\) 为未填的字符,因为非叶节点的出现次数 \\(\\leq\\frac{m}{2}\\) 而 \\(c\\geq\\frac{m}{2}\\),所以一定能填满。然后把 \\(c\\) 填入这一层的叶节点,剩下的就只有另一种颜色的,填入到其它点中,不难发现只有当前层会多一种不同的字符。
现在的问题是判断能不能每行填入相同的字符,这是个背包问题,物品就是每一层的节点数,但是直接做是 \\(O(n^2)\\) 的,优化背包需要观察一下题目性质。因为物品的种类数最多是 \\(\\sqrt n\\) 的,而我们求解的是存在性问题,多重背包是可以 \\(O(1)\\) 跑的,只要记录这一种物品还能取多少,就可以类似完全背包跑了,时间复杂度 \\(O(n\\sqrt n)\\)
总结
我总结不动了,构造法的使用总是出其不意。
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<\'0\' || c>\'9\') {if(c==\'-\') f=-1;}
while(c>=\'0\' && c<=\'9\') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,x,y,tot,f[M],d[M],son[M];
int a[M],b[M],cnt[M],dp[500][M];
vector<int> v[M];char ans[M];
struct edge
{
int v,next;
}e[2*M];
void dfs(int u,int fa)
{
son[u]=0;
d[u]=d[fa]+1;
v[d[u]].push_back(u);
m=max(m,d[u]);
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);son[u]=1;
}
}
void work(int k,int n)
{
if(!k) return ;
int sl=a[b[k]]-dp[k][n];
cnt[b[k]]=sl;
work(k-1,n-sl*b[k]);
}
signed main()
{
n=read();x=read();y=n-x;
for(int i=2;i<=n;i++)
{
int j=read();
e[++tot]=edge{i,f[j]},f[j]=tot;
e[++tot]=edge{j,f[i]},f[i]=tot;
}
dfs(1,0);
for(int i=1;i<=m;i++)
a[v[i].size()]++;
memset(dp,-1,sizeof dp);
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
if(!a[i]) continue;
b[++k]=i;
for(int j=0;j<=n;j++)
{
if(dp[k-1][j]>=0) dp[k][j]=a[i];
if(j>=i) dp[k][j]=max(dp[k][j],dp[k][j-i]-1);
}
}
if(dp[k][x]>=0)
{
work(k,x);
for(int i=1;i<=n;i++) ans[i]=\'b\';
for(int i=1;i<=m;i++)
{
int len=v[i].size();
if(cnt[len])
{
cnt[len]--;
for(auto x:v[i]) ans[x]=\'a\';
}
}
printf("%d\\n",m);
printf("%s\\n",ans+1);
return 0;
}
printf("%d\\n",m+1);
for(int i=1;i<=m;i++)
{
int len=v[i].size();
if(len<=x)
{
for(auto j:v[i]) ans[j]=\'a\';
x-=len;
}
else if(len<=y)
{
for(auto j:v[i]) ans[j]=\'b\';
y-=len;
}
else if(x>y)
{
for(auto j:v[i])
if(son[j]) ans[j]=\'a\',x--;
for(auto j:v[i])
if(!ans[j] && x) ans[j]=\'a\',x--;
for(auto j:v[i])
if(!ans[j]) ans[j]=\'b\';
}
else
{
for(auto j:v[i])
if(son[j]) ans[j]=\'b\',y--;
for(auto j:v[i])
if(!ans[j] && y) ans[j]=\'b\',y--;
for(auto j:v[i])
if(!ans[j]) ans[j]=\'a\';
}
}
printf("%s",ans+1);
}
以上是关于Codeforces Round #699 (Div. 2)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #436 E. Fire(背包dp+输出路径)
[ACM]Codeforces Round #534 (Div. 2)
Codeforces Round #726 (Div. 2) B. Bad Boy(贪心)