Preface
这场其实是上周四 VP 的,因为当时马上要出发打济南站了,但因为挺久没写代码了所以打算临阵磨枪一下
好家伙结果 Div.2 D 不会做直接给人整麻了,不过好在看了眼榜把 E2 写了,后面发现这个 D 想到了就不难
A. Meaning Mean
容易发现从小到大操作一定最优
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=55;
int t,n,a[N];
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d",&n);
for (RI i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+n+1); int ans=a[1];
for (RI i=2;i<=n;++i) ans=(ans+a[i])/2;
printf("%d\n",ans);
}
return 0;
}
B. Maximize Mex
由于 \(n\) 个数的 mex
最大就是 \(n\),因此我们从小到大贪心复杂度就是对的
具体地,用一个桶记一下每个数的数量,如果出现次数超过 \(1\) 就把多出的移动到后面即可
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,x,a[N],c[N];
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d%d",&n,&x);
for (RI i=0;i<=n;++i) c[i]=0;
for (RI i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if (a[i]<=n) ++c[a[i]];
}
int mex=0;
while (c[mex]>0)
{
if (mex+x<=n) c[mex+x]+=c[mex]-1;
++mex;
}
printf("%d\n",mex);
}
return 0;
}
C1. Adjust The Presentation (Easy Version)
简单猜一手结论,发现只保留 \(\{b\}\) 序列中每种数第一次出现的一个,然后看剩下的部分是否为 \(\{a\}\) 的前缀即可
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,m,q,a[N],b[N],vis[N];
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d%d%d",&n,&m,&q);
for (RI i=1;i<=n;++i) scanf("%d",&a[i]),vis[i]=0;
vector <int> vec;
for (RI i=1;i<=m;++i)
{
scanf("%d",&b[i]);
if (!vis[b[i]]) vec.push_back(b[i]),vis[b[i]]=1;
}
bool flag=1;
for (RI i=0;i<vec.size();++i)
if (vec[i]!=a[i+1]) { flag=0; break; }
puts(flag?"YA":"TIDAK");
}
return 0;
}
C2. Adjust The Presentation (Hard Version)
考虑带修怎么做,不妨将上面的结论换个形式描述
令 \(c_i\) 表示 \(a_i\) 在 \(\{b\}\) 序列中第一次出现的位置(没出现就置为 \(m+1\)),显然有解的充要条件为 \(\forall i\in[1,n-1],c_i\le c_{i+1}\)
由于单点操作一次会影响的位置很少,可以拿 set
维护每种数出现的位置,然后讨论修改后的影响即可
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,m,q,a[N],b[N],pos[N],val[N]; set <int> s[N];
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d%d%d",&n,&m,&q);
for (RI i=1;i<=n;++i)
scanf("%d",&a[i]),pos[a[i]]=i,s[i].clear(),s[i].insert(m+1);
for (RI i=1;i<=m;++i)
scanf("%d",&b[i]),s[b[i]].insert(i);
for (RI i=1;i<=n;++i) val[pos[i]]=*s[i].begin();
int cnt=0;
auto calc=[&](CI x)
{
if (x<1||x>=n) return 0;
return (int)(val[x]<=val[x+1]);
};
for (RI i=1;i<n;++i) cnt+=calc(i);
puts(cnt==n-1?"YA":"TIDAK");
while (q--)
{
int x,y; scanf("%d%d",&x,&y);
vector <int> key={pos[b[x]],pos[b[x]]-1,pos[y],pos[y]-1};
sort(key.begin(),key.end());
key.erase(unique(key.begin(),key.end()),key.end());
for (auto it:key) cnt-=calc(it);
s[b[x]].erase(x); s[y].insert(x);
val[pos[b[x]]]=*s[b[x]].begin();
val[pos[y]]=*s[y].begin();
for (auto it:key) cnt+=calc(it);
b[x]=y; puts(cnt==n-1?"YA":"TIDAK");
}
}
return 0;
}
D. Boss, Thirsty
挺神秘的一个题,需要对限制有一定的观察才能入手
朴素的按行 DP 复杂度一眼寄,考虑这种方法不行的根本原因就是状态需要记录区间两个端点,这样显然没有前途
假设上一行选择的区间为 \([l_1,r_1]\),这行选择的区间为 \([l_2,r_2]\),不妨把左端点 \(l_2\) 固定下来,看下此时右端点需要满足哪些性质
- 若 \(l_2<l_1\),此时所有 \(r_2\ge l_1\) 的区间都是合法的;
- 若 \(l_2=l_1\),此时所有 \(r_2>r_1\) 的区间都是合法的;
右端点固定的情况同理,根据这个性质我们发现两个端点的转移其实十分独立
具体地,令 \(f_{i,j,0/1}\) 表示第 \(i\) 行的左/右端点为 \(j\) 时的最大贡献,转移画画图用前缀和优化一下非常显然
#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,INF=1e18;
int t,n,m,a[N],pfx[N],mnp[N],mxs[N],f[2][2][N];
signed main()
{
for (scanf("%lld",&t);t;--t)
{
scanf("%lld%lld",&n,&m);
for (RI p=0;p<2;++p) for (RI q=0;q<2;++q)
for (RI i=1;i<=m;++i) f[p][q][i]=-INF;
pfx[0]=0; mnp[0]=0; mxs[m+1]=-INF;
for (RI i=1;i<=m;++i) scanf("%lld",&a[i]);
for (RI i=1;i<=m;++i) pfx[i]=pfx[i-1]+a[i];
for (RI i=1;i<=m;++i) mnp[i]=min(mnp[i-1],pfx[i]);
for (RI i=m;i>=1;--i) mxs[i]=max(mxs[i+1],pfx[i]);
for (RI i=1;i<=m;++i)
{
f[1][0][i]=mxs[i]-pfx[i-1];
f[1][1][i]=pfx[i]-mnp[i-1];
}
// for (RI i=1;i<=m;++i) printf("%lld%c",f[1][0][i]," \n"[i==m]);
// for (RI i=1;i<=m;++i) printf("%lld%c",f[1][1][i]," \n"[i==m]);
for (RI j=2;j<=n;++j)
{
int now=j&1,lst=now^1;
for (RI i=1;i<=m;++i) f[now][0][i]=f[now][1][i]=-INF;
for (RI i=1;i<=m;++i) scanf("%lld",&a[i]);
for (RI i=1;i<=m;++i) pfx[i]=pfx[i-1]+a[i];
for (RI i=1;i<=m;++i) mnp[i]=min(mnp[i-1],pfx[i]);
for (RI i=m;i>=1;--i) mxs[i]=max(mxs[i+1],pfx[i]);
int tmp1=-INF,tmp2=-INF;
for (RI i=m;i>=1;--i)
{
tmp1=max(tmp1,mxs[i+1]+f[lst][0][i+1]);
tmp2=max(tmp2,mxs[i+1]+f[lst][1][i]);
f[now][0][i]=max(f[now][0][i],tmp1-pfx[i-1]);
f[now][0][i]=max(f[now][0][i],tmp2-pfx[i-1]);
}
tmp1=-INF; tmp2=-INF;
for (RI i=1;i<=m;++i)
{
tmp1=max(tmp1,f[lst][0][i]-(i-2>=0?mnp[i-2]:INF));
tmp2=max(tmp2,f[lst][1][i-1]-(i-2>=0?mnp[i-2]:INF));
f[now][1][i]=max(f[now][1][i],tmp1+pfx[i]);
f[now][1][i]=max(f[now][1][i],tmp2+pfx[i]);
}
// for (RI i=1;i<=m;++i) printf("%lld%c",f[now][0][i]," \n"[i==m]);
// for (RI i=1;i<=m;++i) printf("%lld%c",f[now][1][i]," \n"[i==m]);
}
int ans=-INF;
for (RI i=1;i<=m;++i) ans=max(ans,max(f[n&1][0][i],f[n&1][1][i]));
printf("%lld\n",ans);
}
return 0;
}
E1. Digital Village (Easy Version) && E2. Digital Village (Hard Version)
这种涉及到路径上最大值的问题一眼克鲁斯卡尔重构树,因此考虑把重构树先搞出来
将选中的点成为关键点,则某个需要网络的点对应的贡献,就是重构树上距离其最近的且子树里含有关键点的祖先对应的权值
不难发现这个东西很适合 DP,令 \(f_{i,j}\) 表示点 \(i\) 的子树里选了 \(j\) 个关键点时对应的贡献,转移类似树上背包,不过由于重构树是棵二叉树因此写起来比较方便
需要注意的是当某一边子树中没有关键点时就要在这个点出合并贡献,维护子树内需要网络的点的个数后就很容易转移
总复杂度同树上背包,\(O(n^2)\),E3 估计要神秘多项式优化,弃疗
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<array>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=10005,INF=1e18;
int t,n,m,p,key[N],w[N],sz[N],x,y,z,fa[N],ls[N],rs[N]; vector <int> f[N];
inline int getfa(CI x)
{
return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
inline void DFS(CI now)
{
sz[now]=key[now];
if (ls[now]==0) return (void)(f[now]={0,0});
DFS(ls[now]); DFS(rs[now]);
sz[now]+=sz[ls[now]]+sz[rs[now]];
f[now].resize(sz[now]+1);
for (RI i=1;i<=sz[now];++i) f[now][i]=INF;
for (RI i=1;i<=sz[ls[now]];++i)
for (RI j=1;j<=sz[rs[now]];++j)
f[now][i+j]=min(f[now][i+j],f[ls[now]][i]+f[rs[now]][j]);
for (RI i=1;i<=sz[ls[now]];++i)
f[now][i]=min(f[now][i],f[ls[now]][i]+sz[rs[now]]*w[now]);
for (RI j=1;j<=sz[rs[now]];++j)
f[now][j]=min(f[now][j],f[rs[now]][j]+sz[ls[now]]*w[now]);
}
signed main()
{
for (scanf("%lld",&t);t;--t)
{
scanf("%lld%lld%lld",&n,&m,&p);
for (RI i=1;i<=2*n;++i) ls[i]=rs[i]=key[i]=0,fa[i]=i;
for (RI i=1;i<=p;++i) scanf("%lld",&x),key[x]=1;
vector <array <int,3>> E;
for (RI i=1;i<=m;++i)
scanf("%lld%lld%lld",&x,&y,&z),E.push_back({z,x,y});
sort(E.begin(),E.end()); int idx=n;
for (auto [z,x,y]:E)
{
if (getfa(x)==getfa(y)) continue;
w[++idx]=z;
ls[idx]=getfa(x); rs[idx]=getfa(y);
fa[getfa(x)]=fa[getfa(y)]=idx;
}
DFS(idx);
for (RI i=1;i<=n;++i) printf("%lld%c",i<=p?f[idx][i]:0," \n"[i==n]);
}
return 0;
}
Postscript
本来今天打算再开一场 VP 的,但结果 CF 好巧不巧爆炸了,看评测队列的样子感觉要炸很久,这下不得不白兰了
标签:977,include,based,int,RI,const,now,Round,define From: https://www.cnblogs.com/cjjsb/p/18518737