P2057 [SHOI2007] 善意的投票 / [JLOI2010] 冠军调查

101 阅读2分钟

题目描述

幼儿园里有 n 个小朋友打算通过投票来决定睡不睡午觉。

对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。

虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。

我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。

我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?

输入格式

第一行两个整数 �,�n,m。其中 �n 代表总人数,�m 代表好朋友的对数。

第二行 �n 个整数,第 �i 个整数代表第 �i 个小朋友的意愿:当它为 11 时表示同意睡觉,当它为 00 时表示反对睡觉。

接下来 �m 行,每行有两个整数 �,�i,j,表示 �,�i,j 是一对好朋友,我们保证任何两对 �,�i,j 不会重复。

输出格式

一行一个整数,即可能的最小冲突数。

输入输出样例

输入 #1复制

3 3
1 0 0
1 2
1 3
3 2

输出 #1复制

1

说明/提示

对于 100%100% 的数据,2≤�≤3002≤n≤300,1≤�≤�(�−1)21≤m≤2n(n−1)​。

Code

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
#include <stack>
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define int long long
#define endl '\n'
#define pb push_back
#define NO cout << "NO" << endl;
#define YES cout << "YES" << endl;
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<VI> VII;
ll MOD = 998244353;
ll powmod(ll a,ll b) {ll res=1;a%=MOD; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
mt19937 mrand(random_device{}()); 
const int N = 2e6 + 10;
int w,dis[N];
int tot=1,now[N],head[N]; 
int inf = 1ll << 62;
struct node {
   int to,net;
   int val;
} e[N];

int n, m, s, t;

inline void add(int u,int v,int w) {
   e[++tot].to=v;
   e[tot].val=w;
   e[tot].net=head[u];
   head[u]=tot;
   
   e[++tot].to=u;
   e[tot].val=0;
   e[tot].net=head[v];
   head[v]=tot;
}

inline int bfs() {  //在惨量网络中构造分层图 
   memset(dis, -1, sizeof(dis));
   queue<int> q;
   q.push(s);
   dis[s]=0;
   now[s]=head[s];
   while(!q.empty()) {
      int x=q.front();
      q.pop();
      for( int i=head[x];i;i=e[i].net) {
         int v=e[i].to;
         if(e[i].val>0&&dis[v]==-1) {
            q.push(v);
            now[v]=head[v];
            dis[v]=dis[x]+1;
         }
      }
   }
   return dis[t] != -1;
}

inline int dfs(int x,int sum) {  //sum是整条增广路对最大流的贡献
   if(x==t) return sum;
   int k,res=0;  //k是当前最小的剩余容量 
   for( int i=now[x];i&&sum;i=e[i].net) {
      now[x]=i;  //当前弧优化 
      int v=e[i].to;
      if(e[i].val>0&&(dis[v]==dis[x]+1)) {
         k=dfs(v,min(sum,e[i].val));
         if(k==0) continue;  //剪枝,去掉增广完毕的点 
         e[i].val-=k;
         e[i^1].val+=k;
         res+=k;  //res表示经过该点的所有流量和(相当于流出的总量) 
         sum-=k;  //sum表示经过该点的剩余流量 
         if (!sum) break;
      }
   }
   if (!sum) dis[x] = -1;
   return res;
}
/* while(bfs()) {
      ans+=dfs(s,inf);  //流量守恒(流入=流出) 
   }
*/
void solve()
{
   cin >> n >> m;
   s = 0, t = n + 1;
   for (int i = 1; i <= n; i++) {
      int x; cin >> x;
      if (!x) add(i, t, 1);
      else add(s, i, 1);
   }
   for (int i = 1; i <= m; i++) {
      int u, v; cin >> u >> v;
      add(u, v, 1);
      add(v, u, 1);
   }
   ll ans = 0;
   while (bfs()) {
      ans += dfs(s, inf);
   }
   cout << ans << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0); 
    // int T;cin >> T;
    // while ( T -- )
    solve();
    return 0;
}