Title

CF990F Flow Control 题解

解题思路

一个很显然的结论:

  • 当且仅当所有点的流量之和为 0 时,存在解。

在得出这个结论后,我们可以先计算出 i=1nsi 的值,然后判断一下是否为 0,如果不为 0,那么直接输出 Impossible 即可。

接下来讨论为 0 的情况。根据上面的结论我们可以知道,如果图联通,那么一定满足这个结论,反之亦然。由此可得,我们可以删除若干条边,只要剩下的边可以构成一个联通图就可以了。这里考虑最简单的情况,即只保留 n1 条边,剩下的边构成一棵无根树,我们默认 1 号节点为它的根节点。从根开始搜索,直到找到叶子节点,然后我们根据叶子节点的约束条件,计算它的父节点还需要流入或流出的水量,然后一层层往上更新即可,每次更新的时候同时处理出当前节点和它的父亲节点连边的流量。因为一条边的水量的正负由边的方向决定,所以我们还需要记录每一条边,可以用 map 来存储两个点之间的边的编号,按照输入中给的方向的边,编号为正,否则编号为负,这样在更新的时候就可以根据边的编号的正负来判断水流的方向,从而确定 fi 的正负。

时间复杂度 O(nlogn)

AC 代码

#include<iostream>
#include<math.h>
#include<time.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include <map>
#define ll long long

inline void FileOpen(const char *infile,const char *outfile){
    char *fileread=new char[1];
    char *filewrite=new char[1];
    fileread[0]=114,filewrite[0]=119;
    freopen(infile,fileread,stdin);
    freopen(outfile,filewrite,stdout);
}
inline void FileClose(){
    char *fileread=new char[1];
    char *filewrite=new char[1];
    char *CONwrite=new char[3];
    fileread[0]=114,filewrite[0]=119;
    CONwrite[0]=67,CONwrite[1]=79,CONwrite[2]=78;
    fclose(stdin);
    fclose(stdout);
    std::cin.clear();
    std::cout.clear();
    freopen(CONwrite,fileread,stdin);
    freopen(CONwrite,filewrite,stdout);
}
#define N 200005
#define pii std::pair<int,int>
int n,m,s[N],f[N];
int fl[N],vis[N];
std::vector<int> edge[N];
std::map<pii,int> pos;
inline void dfs(int u,int fa){
    fl[u]=s[u];vis[u]=1;
    for(auto v:edge[u]){
        if(v==fa) continue;
        if(vis[v]) continue; 
        dfs(v,u);fl[u]+=fl[v];
    }if(fl[u]==0) return;
    int np=pos[{u,fa}];
    if(np<0)
        f[-np]=fl[u];
    else f[np]=-fl[u];
}
inline void work(){
    scanf("%d",&n);int sum=0;
    for(register int i=1;i<=n;++i)
        scanf("%d",&s[i]),sum+=s[i];
    if(sum!=0){puts("Impossible");return;}
    scanf("%d",&m);int u,v;
    for(register int i=1;i<=m;++i){
        scanf("%d%d",&u,&v);
        edge[u].push_back(v);
        edge[v].push_back(u);
        pos[{u,v}]=i;
        pos[{v,u}]=-i;
    }dfs(1,0);puts("Possible");
    for(register int i=1;i<=m;++i)
        printf("%d\n",f[i]);
}signed main(){
    srand(114514);
    srand(rand());
    srand(time(0));

    work();system("pause");
}
posted @   UncleSam_Died  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示