一个假做法,求错误原因
查看原帖
一个假做法,求错误原因
619237
LY00000楼主2024/11/10 08:18
#include <bits/stdc++.h>
#define F(i,l,r) for(int i=(l);i<=(r);i++)
#define FF(i,r,l) for(int i=(r);i>=(l);i--)
#define ll long long
#define M 200010
using namespace std;
int n;
struct edge{
    int v,to;
}e[M];int head[M],tnt;
void add_edge(int x,int y){
    e[++tnt].v=y;e[tnt].to=head[x];head[x]=tnt;
}
int ans=2147483647;
int siz[M],hson[M],hpos[M];
int fa[M];
int gp[M],dp[M];
int maxd[M],mint[M];
int op;
void dfs(int u,int f){
    fa[u]=f;
    for(int i=head[u];i;i=e[i].to){
        int v=e[i].v;if(v==f)continue;
        dfs(v,u);siz[u]+=siz[v];
        if(hson[u]<siz[v]){
            hson[u]=siz[v];hpos[u]=v;
        }
    }
    siz[u]++;if(siz[u]==1){dp[u]=u;return ;}
    int v=hpos[u];
    while(dp[v]!=u){
        int k=siz[dp[v]];
        if(abs(siz[u]-2*k)<gp[u]){
            maxd[u]=max(siz[u]-k,k);
            mint[u]=min(siz[u]-k,k);
            gp[u]=maxd[u]-mint[u];
            dp[u]=dp[v];
        }
        if(k>siz[u]/2)break;
        else dp[v]=fa[dp[v]];
    }
}
void dfs1(int u){
    int p=siz[1]-siz[u];
    int m1=max(maxd[u],p)-min(mint[u],p);
    ans=min(ans,m1);
    if(!hpos[u])return ;
    dfs1(hpos[u]);
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
	cin>>n;memset(gp,0x3f,sizeof(gp));
    F(i,1,n-1){
        int x,y;cin>>x>>y;add_edge(x,y);add_edge(y,x);
    }dfs(1,1);
    dfs1(1);
	cout<<ans<<'\n';
	
	
	return 0;
}

(以下是我赛时想法) 我的策略是:先切一刀,考虑第二刀

假设第二刀是随便切的,那么令切下来的两个连通块为大小m1,m2(m1<m2);再令第一刀切下来的的部分大小为m。令m为已知量。

若m<m1 则min=m,max=m2,那么要让答案最小,就要缩小m2

若m1<m<m2,则min=m1,max=m2,同理,要缩小m2,增大m1

若m>m2,则min=m1,max=m,同理,要缩小m2,增大m1

综上,对于第二刀切的任意子树,都要使m1,m2尽量接近,故最佳状况为m1=m2;

所以显然第二刀切在该子树的重儿子上

所以有一种做法,就是对每棵子树枚举他重儿子的切点,期望nlgn

但是遇到极限数据(链)会被卡掉

又因为对于每个子树,其切点必然在其重儿子之上,所以我们可以对每个子树的切点进行转移

每次将重儿子的切点向上移,计算并将其最优解切点位置转移,最后再dfs一下统计答案即可

但是假了呢,肿么回事能

2024/11/10 08:18
加载中...