[数学][构造]JZOJ 3317 管道

Description

Hotham市再次被Jester袭击。这一次,Jester攻击的目标是Hotham的供水系统。Hotham的淡水存储在N个水库,由M个管道连接。

任意2个水库之间至少有一条路径,该路径可能包含多个管道。每个管道连接两个不同的水库,任何两个水库之间最多只有一个管道。Jester破坏了一些管道进行排水。管道的排水量为偶数立方米/秒。如果连接水库u和水库v的管道排水量为2D立方米/秒,那么水库u和水库v排水量分别为D立方米/秒。Jester可以也破坏一些管道进行注水。同样,连接水库u和v的管道注入水量为2P立方米/秒,则水库u和水库v注水量分别为P立方米/秒。

每个水库供水量的净变化是由连接到它的管道的排水量和注水量的总和。如果连接该水库的管道排水量分别为2D1, 2D2,…,2Da立方米/秒, 注水量分别为2P1, 2P2 ,…,2Pb 立方米/秒。该水库水量的净变化为P1 + P2 + ... + Pb -D1 - D2 –Da。

Hotham市长已在水库安装了传感器,但不是在管道中。因此,他可以观察到在每个水库水的净变化,但并知道有多少水被排出或注入各个管道。

给出水管连接图和每个水库供水量的净变化,编写程序决定这些信息是否能确定一个唯一的计划。如果各个水管排出或注入的水量是唯一的,那么该计划可以被唯一确定。请注意,并不是每一个水管中的注水量和排水量都相等。如果恰好有一个可能性,你的程序应该打印出来。
 

Input

输入的第一行包含两个整数:N:Hotham的水库数 和M:管道数。

接下来的N行每行包含一个整数ci:表示水库i(1≤i≤N)的水量净变化。

再接下来的M行每行包含两个整数ui和vi(1≤i≤M)。每一行分别表示,水库 ui和vi之间有管道连接(1≤ui,vi≤N)。 

Output

如果Jester的计划不能被唯一确定,你的程序则输出0。否则,你的程序应该输出M行,每一行包含一个整数Xi(1≤i≤M)。 如果Jester从连接水库ui和vi间的管道里排水,该管道的排水量为di立方米/秒,则设Xi=-di。如果Jester往连接水库ui和vi间的管道里注水,该管道的注水量为pi立方米/秒,则Xi=pi。如果Jester对连接水库ui和vi间的管道既不排水也不注水,则Xi =0。
 

Sample Input

输入1:
4 3
-1
1
-3
1
1 2
1 3
1 4
输入2:
4 5
1
2
1
2
1 2
2 3
3 4
4 1
1 3

Sample Output

输出1:
2
-6
2
输出2:
0
 

Data Constraint

1<=N<=100 000

1<=M<=500 000

-10^9<=Ci<=10^9

如果方案唯一,则-10^9<=Xi<=10^9

30%的数据满足供水系统的网络是一棵树。

分析

设e[u,v]为连接u,v的边的值的一半

则有:$c[u]=\Sigma e[u,v]$这么多个方程组

m=n-1就是棵树,树只要从叶子节点推上来就一定有唯一解

那么m>n时,变量数大于方程数,定然无解

重点讨论m=n

m=n一定是一个环伸出多条边,把边的答案计算完,留下计算过后的环的v

很明显,对于一个点$c[u]=e(u,v1)+e(v2,u)$

不好解,我们考虑消去变量:e(v2,u)+e(u,v1)-e(u,v1)-e(v1,v3)+e(v1,v3)+e(v3,v4)……=c[u]-c[v1]+c[v3]-……

这样构造到最后,显然$ans=2*e(v2,u)=\Sigma c[u]*(-1)^dep$

 

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct Graph {
    int v,nx,id;
}g[2*N];
int cnt,list[N],c[N],ans[N],deg[N];
bool b[2*N];
int n,m,p;

void Add(int u,int v,int id) {
    g[++cnt]=(Graph){v,list[u],id};list[u]=cnt;
    g[++cnt]=(Graph){u,list[v],id};list[v]=cnt;
}

void Topposort() {
    queue<int> q;
    while (!q.empty()) q.pop();
    for (int i=1;i<=n;i++) if (deg[i]==1) q.push(i),p--;
    while (!q.empty()) {
        int u=q.front();q.pop();
        for (int i=list[u];i;i=g[i].nx)
            if (deg[g[i].v]) {
                if (--deg[g[i].v]==1) q.push(g[i].v),p--;
                ans[g[i].id]=2*c[u];
                c[g[i].v]-=c[u];c[u]=0;
                deg[u]--;
            }
    }
}

void Pre(int u,int fa) {
    for (int i=list[u];i;i=g[i].nx)
        if (deg[g[i].v]&&g[i].v!=fa&&!b[i]) {
            b[i]=1;
            Pre(g[i].v,u);
            return;
        }
}

void DFS(int u,int fa,int I,int dep) {
    ans[g[I].id]+=((dep&1)?c[u]:-1*c[u]);
    for (int i=list[u];i;i=g[i].nx)
        if (b[i]) {
            if (i==I) return;
            DFS(g[i].v,u,!I?i:I,dep+1);
            return;
        }
}

void Calc(int u,int fa,int from,int dep) {
    for (int i=list[u];i;i=g[i].nx)
        if (b[i]) {
            if (dep==p) return;
            if (fa) ans[g[i].id]=-(ans[g[from].id]-c[u])+c[u];
            Calc(g[i].v,u,i,dep+1);
            return;
        }
}

int main() {
    scanf("%d%d",&n,&m);
    if (m>n) {
        printf("0");
        return 0;
    }
    for (int i=1;i<=n;i++) scanf("%d",&c[i]);
    for (int i=1,u,v;i<=m;i++) {
        scanf("%d%d",&u,&v);
        deg[u]++;deg[v]++;
        Add(u,v,i);
    }
    p=n;
    Topposort();
    if (m==n-1) {
        for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
        return 0;
    }
    if (!(p%2)) {
        printf("0");
        return 0;
    }
    for (int i=1;i<=n;i++)
        if (deg[i]) {
            Pre(i,0);
            break;
        }
    for (int i=1;i<=n;i++)
        if (deg[i]) {
            DFS(i,0,0,0);
            Calc(i,0,0,0);
            break;
        }
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
View Code

 

posted @ 2019-07-05 07:31  Vagari  阅读(221)  评论(0编辑  收藏  举报