Codeforces 2115B Gellyfish and Camellia Japonica 题解 [ 蓝 ] [ 图论建模 ] [ 拓扑排序 ]

Gellyfish and Camellia Japonica:以此纪念把我送上 CM 的一道题。

不难发现由于这些变量是具有传递性的,所以可以把序列上每个位置的每次修改都看做一个节点,对于一次 \((x,y,z)\) 的修改,就将 \(x,y\) 的当前版本向 \(z\) 的当前版本连边,以表示两者取 \(\min\) 的赋值操作。

进而考虑在每个节点的最终版本已知的情况下如何倒推出其余节点。先来弱化限制,假设节点 \(v\) 的值 \(a\) 已经确定,则对于给 \(v\) 赋值的两个节点 \(u_1,u_2\) 都必须满足值 \(\ge a\) 的下界限制,因为如果小于 \(a\) 则无法让 \(v\) 取到 \(a\)

于是可以建反图后,跑一遍拓扑排序,递推出每个节点能取到的最小值,然后惊奇的发现让每个点取最小值正是一种构造方式!因为每个节点的下界已经被满足了,为了让被赋值的点能恰好赋到对应值而不变大,所以尽可能取最小值以满足赋值上界是一定是不劣的。

最后在构造出方案后,自行模拟一下判断方案是否合法即可。

时间复杂度 \(O(n+q)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=1000005,inf=0x3f3f3f3f,M=2000005;
int n,q,pos[N],id,dp[N],b[N],qx[N],qy[N],qz[N],tmp[N];
int h[N],rd[N],idx;
struct Edge{
    int v,ne;
}e[M];
void add(int u,int v)
{
    e[++idx]={v,h[u]};
    h[u]=idx;
}
void init()
{
    idx=0;
    for(int i=1;i<=id;i++)
    {
        h[i]=0;
        dp[i]=0;
        rd[i]=0;
    }
    id=0;
}
bool check()
{
    for(int i=1;i<=n;i++)
        tmp[i]=dp[i];
    for(int i=1;i<=q;i++)
    {
        int x=qx[i],y=qy[i],z=qz[i];
        tmp[z]=min(tmp[x],tmp[y]);
    }
    for(int i=1;i<=n;i++)
        if(tmp[i]!=b[i])return 0;
    return 1;
}
void solve()
{
    init();
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        pos[i]=++id;
    }
    for(int i=1;i<=q;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&qx[i],&qy[i],&qz[i]);
        x=qx[i],y=qy[i],z=qz[i];
        int u=++id;
        add(u,pos[x]);
        add(u,pos[y]);
        rd[pos[x]]++;
        rd[pos[y]]++;
        pos[z]=u;
    }
    for(int i=1;i<=n;i++)
        dp[pos[i]]=b[i];
    queue<int>q;
    for(int i=1;i<=id;i++)
        if(rd[i]==0)q.push(i);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=h[u];i;i=e[i].ne)
        {
            int v=e[i].v;
            rd[v]--;
            if(rd[v]==0)q.push(v);
            dp[v]=max(dp[v],dp[u]);
        }
    }
    if(check()==0)
    {
        printf("-1\n");
        return;
    }
    for(int i=1;i<=n;i++)printf("%d ",dp[i]);
    printf("\n");
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    init();
    int t;
    scanf("%d",&t);
    while(t--)solve();
    return 0;
}
posted @ 2025-06-04 23:15  KS_Fszha  阅读(54)  评论(0)    收藏  举报