【思维题】【细节】AGC010 C——Cleaning

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34283998/article/details/82843540

题目传送门
这道题应该是比较简单的,但是有很多限制条件需要考虑进去,所以很容易漏掉一些条件.
首先我们只考虑一种比较基本的情况一个根节点和它的若干儿子.
在这里插入图片描述
对于节点u,维护出它儿子上的石头的和sum,以及一个石头数最多的儿子mx.
那么可以推出一些令整棵树满足的条件:

  1. sum>=a[u] (a[u]是节点u上的石头数量) 如果不满足,那么意味着即使u的所有的儿子都与u的子树外的一个节点进行移除操作,也不能够将u上面的石头数移完.
  2. sum<=2*a[u] 如果不满足,那么意味着即使u的所有儿子都与u的子树内的一个节点进行移除操作,u上面的石头数量都是不够的.
  3. mx<=a[u] 如果不满足,那么意味着这个最大的儿子不论是在u的子树内部解决还是传到不是u的子树上都无法将石头移完.

刚刚说明不满足上面三个条件一定是无解,但是并没有说明满足这三个条件就一定能够找到一种合法的方案成功.
可以将上述的问题看作,证明在满足条件1,2时,满足条件3则一定可以找到一种合法方案.

证明:因为mx满足不大于sum的二分之一,所以我们可以将它和次大的进行操作,直到次大的和次次大的相同,然后我们将mx剩余的部分进行消耗.那么最后的状态依旧变为一个新的mx,不大于新的sum的二分之一.依次递归下去即可解决.
例如:
u一共有三个儿子,石头数分别为,49,44,21.
首先将,44弄到21,那么剩下(26,21,21)
然后我们就需要想办法将26全部消耗掉,在此过程中保证次大和次次大相差不超过1,然后剩下(0,8,8)
最后两个8之间相互匹配即可.

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;
const int MAXN=1e5+5;

int n;
ll a[MAXN];
vector<int> E[MAXN];

inline void err(){puts("NO"); exit(0);}

void dfs(int u,int fa=0){
    if(E[u].size()==1) return;
    ll sum=0,mx=0;
    for(int i=0;i<(int)E[u].size();i++){
        int v=E[u][i];
        if(v==fa) continue;
        dfs(v,u);
        sum+=a[v];
        mx=max(mx,a[v]);
    }
    if(sum<a[u]||sum>2*a[u]) err();
    if(mx>a[u]) err();
    a[u]=2*a[u]-sum;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    if(n==2){
        if(a[1]!=a[2]) err();
        puts("YES");
        exit(0);
    }
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        E[u].push_back(v);
        E[v].push_back(u);
    }
    int st=1;
    for(;E[st].size()==1;st++);
    dfs(st);
    if(!a[st]) puts("YES");
    else puts("NO");
}

虽然这道题解决了,但是想要出这道题的数据还是有些困难.尤其是想要构造出针对每一个限制条件的数据.
普通的随机数直接刷是不行的,有极大地概率刷到无解的情况.因此在这里提出一种可以得到有解数据的方法,但是依旧没有想到如何生成有针对条件的数据.

首先利用并查集构造出一棵树,将叶子节点以随机顺序放入队列,然后每次选择当前节点和队列中下一个节点进行放石头的操作.需要在这两个点的路径上都加上石头数,所以利用树上差分实现.在两个叶子节点处,加上石头数,在lca及lca的父亲处减去石头数.然后,每一个节点的答案就为它的子树的和.

#include<cstdio>
#include<algorithm>
#include<ctime>
#include<vector>
#include<map>
#include<queue>
#include<windows.h>
using namespace std;

typedef long long ll;
const int MAXN=1e5;
const int MAXM=500;
const int INF=0x3f3f3f3f;

int get(int l,int r){
    if(l>r) return -INF;
    int len=r-l;
    return rand()*rand()%(len+1)+l;
}

int fa[MAXN+5][20],dep[MAXN],cnt[MAXN];
ll a[MAXN+5],op[MAXN+5];
vector<int> E[MAXN+5],t;
map<pair<int,int>,bool> vis;

int find(int x){return fa[x][0]==x?x:fa[x][0]=find(fa[x][0]);}

void dfs(int u,int fa=0){
    ::fa[u][0]=fa;
    dep[u]=dep[fa]+1;
    for(int i=0;i<(int)E[u].size();i++){
        int v=E[u][i];
        if(v==fa) continue;
        cnt[u]++;
        dfs(v,u);
    }
}

bool solve(int u,int fa=0){
    if(E[u].size()==1) return 1;
    ll sum=0,mx=0;
    for(int i=0;i<(int)E[u].size();i++){
        int v=E[u][i];
        if(v==fa) continue;
        if(!solve(v,u)) return 0;
        sum+=a[v];
        mx=max(mx,a[v]);
    }
    if(sum<a[u]||sum>2*a[u]) return 0;
    if(mx>a[u]) return 0;
    a[u]=2*a[u]-sum;
    return 1;
}

int work(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    int log;
    for(log=1;(1<<log)<=dep[x];log++); log--;
    for(int i=log;i>=0;i--)
        if(dep[x]-(1<<i)>=dep[y])
            x=fa[x][i];
    if(x==y) return x;
    for(int i=log;i>=0;i--)
        if(fa[x][i]!=-1&&fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void getans(int u,int fa=0){
    if(E[u].size()==1) return;
    for(int i=0;i<(int)E[u].size();i++){
        int v=E[u][i];
        if(v==fa) continue;
        getans(v,u);
        a[u]+=a[v];
    }
}

int main(){
    srand(time(NULL));

    int n;
    while(1){
        t.clear();
        memset(a,0,sizeof a);
        memset(cnt,0,sizeof cnt);
        memset(dep,0,sizeof dep);
        memset(fa,0,sizeof fa);
        n=get(MAXN,MAXN);
        for(int i=1;i<=n;i++) fa[i][0]=i,E[i].clear();
        for(int id=0;id<n-1;){
            int u=get(1,n),v=get(1,n);
            if(find(u)==find(v)) continue;
            fa[find(u)][0]=find(v);
            E[u].push_back(v);
            E[v].push_back(u);
            id++;
        }
        //for(int i=1;i<n;i++) E[i].push_back(i+1),E[i+1].push_back(i);
        int st=1;
        for(;E[st].size()==1;st++);
        memset(fa,-1,sizeof fa);
        dfs(st);
        for(int j=0;j<20;j++)
            for(int i=1;i<=n;i++)
                if(fa[i][j-1]>=1)
                    fa[i][j]=fa[fa[i][j-1]][j-1];
        for(int i=1;i<=n;i++)
            if(E[i].size()==1)
                t.push_back(i);
        if(t.size()&1) continue;
        for(int i=1;i<(int)t.size();i+=2){
            int lc=work(t[i-1],t[i]);
            int sum=get(1,MAXM);
            a[lc]-=sum;
            a[fa[lc][0]]-=sum;
            a[t[i-1]]+=sum;
            a[t[i]]+=sum;
        }
        getans(st);

        memcpy(op,a,sizeof op);
        if(!solve(st)) continue;
        if(a[st]==0)
        break;
    }

    printf("%d\n",n);
    for(int i=1;i<=n;i++) printf("%lld ",op[i]);
    puts("");
    for(int u=1;u<=n;u++)
        for(int i=0;i<(int)E[u].size();i++){
            int v=E[u][i];
            if(vis[make_pair(u,v)]) continue;
            vis[make_pair(u,v)]=vis[make_pair(v,u)]=1;
            printf("%d %d\n",u,v);
        }
}

猜你喜欢

转载自blog.csdn.net/qq_34283998/article/details/82843540