前言
离 A 掉一道题最近的一次。
过程
看完 T1 以后,想先构一个 \(\mathcal{O}(n^3)\) 的暴力,但是发现只有 10pts,而且算法复杂度接近 \(\mathcal{O}(n^4)\),所以果断放弃。
把链的限制写了(然后亏大了),然后开始在 CS 上面画画。画了一会发现一个简单的 dp 思路,但是是 \(\mathcal{O}(n^2)\) 的。不过分也不少,就写了。调完以后时间已经过了 \(1\frac{1}{12}h\)。
发现我的这个 dp 枚举只在于一个点,所以考虑怎么选点最优。容易想到重心,但是立刻就被 hack 了。
不服气的我发现重心被 hack 的可能性不大(因为当时没有看到数据捆绑,不过也多亏没看到),然后就开始写,结果发现跑过了大样例?
然后在原代码的基础上稍微修饰一下,再测的时候发现第二个样例挂了,说明假了。有点无奈,所以分段跑一下以后就丢了。
赛后发现有 65pts,某猫告诉我我的链判错了。我尝试改链,然后就:
一条链损我 35pts?
纪念一下我的 AC 代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int w=1,s=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
return w*s;
}
const int mod=1e9+7;
const int maxn=2e5+10;
const int inf=1e9+7;
int n,k;
vector<int> G[maxn];
int xo[maxn];
int du[maxn];
void Sub()
{
if(n>=k*2+2)puts("2");
else if(n>=k+1)puts("1");
else puts("0");
return ;
}
int f[maxn][22],d[maxn],fa[maxn],siz[maxn],dp[maxn];
int ans=0;
void pre(int x,int faa)
{
fa[x]=faa;
siz[x]=1;
d[x]=d[faa]+1;
f[x][0]=faa;
for(int i=1;i<=21;i++)f[x][i]=f[f[x][i-1]][i-1];
for(auto y : G[x])
{
if(y==faa) continue;
pre(y,x);
siz[x]+=siz[y];
}
}
int lca(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
for(int i=21;i>=0;i--)
if(d[f[x][i]]>=d[y]) x=f[x][i];
if(x==y) return y;
for(int i=21;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
void dfs(int x,int faa)
{
int ma=0;dp[x]=0;siz[x]=1;
for(auto y : G[x])
{
if(y==faa)continue;
dfs(y,x);siz[x]+=siz[y];
ma=max(ma,dp[y]-(siz[y]>=k));
if(siz[y]>=k)dp[x]++;
}
dp[x]+=ma;
}
void Sub2()
{
pre(1,0);
for(int x=1;x<=n;x++)
{
dfs(x,0);
int ma=-11,maa=-11,anss=0;
for(auto y : G[x])if(siz[y]>=k)anss++;
for(auto y : G[x])
{
int num=dp[y];
if(siz[y]>=k)num--;
if(num>ma)
{
maa=ma;
ma=num;
}
else if(num>maa)
{
maa=max(0ll,num);
}
}
anss=anss+ma+maa;
ans=max(ans,anss);
}
printf("%lld\n",ans);
}
int root,ddp[maxn]={inf};
void get(int x,int fa)
{
siz[x]=1;
for(auto y : G[x])
{
if(y==fa)continue;
get(y,x);siz[x]+=siz[y];
ddp[x]=max(ddp[x],siz[y]);
}
ddp[x]=max(n-siz[x],ddp[x]);
if(ddp[root]>ddp[x])root=x;
}
void Sub3()
{
get(1,0);
dfs(root,0);
int ma=-11,maa=-11,x=root,anss=0;
for(auto y : G[x])if(siz[y]>=k)anss++;
for(auto y : G[x])
{
int num=dp[y];
if(siz[y]>=k)num--;
if(num>ma)
{
maa=ma;
ma=num;
}
else if(num>maa)
{
maa=max(0ll,num);
}
}
anss=anss+ma+maa;
printf("%lld\n",anss);
}
void Main()
{
bool sub=1;
n=read(),k=read();ans=0;root=0;
for(int i=1;i<=n;i++)G[i].clear();
for(int i=1;i<=n;i++)du[i]=dp[i]=siz[i]=d[i]=ddp[i]=0;
for(int i=1;i<n;i++)
{
int x=read(),y=read();
G[x].push_back(y);
G[y].push_back(x);
du[x]++;du[y]++;
if(du[x]>2||du[y]>2)sub=0;
}
if(sub){Sub();return ;}
if(n<=1000){Sub2();return ;}
Sub3();return ;
}
signed main()
{
#ifdef Lydic
freopen(".in","r",stdin);
freopen(".out","w",stdout);
// #else
// freopen("Zephyros.in","r",stdin);
// freopen("Zephyros.out","w",stdout);
#endif
int T=read();
while(T--)Main();
return 0;
}
转去看 T2,光速写完 20pts 的暴力,然后发现很可以二分,推了一大会发现二分复杂度甚至不如暴力的我决定弃掉这道题。
讲课的人说是笛卡尔树思想,但是我不会单调栈。
赛后发现为数不多的 20pts 还爆了 10pts,只剩一份蛋包饭的分数了。
不知道为什么 T3 当时没有细想,现在觉得比 T2 简单,但是考场上放了个 5pts 就过了。这道题 jsy 讲容斥的时候讲过错排思想,针对这道题就是把排列转成多个环以后跑一个分组背包(昨天刚见过)。
T4 的话觉得是原,把原题扒出来以后发现压根没有一点相似之处,小部分数据可以区间 dp,但是当时我拐回去看 T1 了,所以没有写(然后 T1 也爆了)。
总结
还是细节问题,但有一部分也是数据的原因。总的来说比赛挂了 10+35=45pts,不算高,但也不算低。
排名非常靠后,感觉 7 级线有点难。
标签:ma,int,siz,2024,num,maxn,DMY,CSP,dp From: https://www.cnblogs.com/Lydic/p/18438099