codeforces863F Almost Permutation 费用流

题目传送门

题意:

  构造出一个数列,数字在$1~n$的范围内,要求$\sum_{i=1}^n  count(i)^{2}$最小,$count(i)$的意思是数列中i出现的次数。并且数列要满足两种类型的条件,一个是$a_i->a_j$全部大于v,还有$a_i->a_j$全部小于v。

思路:

  首先,我们可以把限制条件转化为某个位置上数值的上限和下限,然后位置和对应能填的所有数字都建容量为1的边,然后每一个数字向汇点建n条容量为1的边,费用分别是$1,3,5,……,2*j-1$,这样建边后,如果一个数字被选取了两次,得到的前缀和刚好就是出现次数的平方。

 

#pragma GCC optimize (2)
#pragma G++ optimize (2)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#include<cstdio>
#include<vector>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,b,a) for(int i=b;i>=a;i--)
#define clr(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pii pair<int,int >
using namespace std;
typedef long long ll;
const int maxn=200010;
const int inf=0x3f3f3f3f;
ll rd()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int MAXN = 1010;
const int MAXM = 20010;
const int INF = 0x3f3f3f3f;
struct Edge {
    int to, next, cap, flow, cost;

} edge[MAXM];
struct pp{
    int u,v,c,w;
}in[MAXN];
int head[MAXN], tol;
int pre[MAXN], dis[MAXN];
bool vis[MAXN];
int N=MAXN-2;
int n,q,l[MAXN],r[MAXN];
void init() {

    tol = 0;
    memset(head, -1, sizeof(head));

}
void addv(int u, int v, int cap, int cost) {
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;

}
bool spfa(int s, int t) {
    queue<int>q;
    clr(dis,INF);
    clr(vis,0),clr(pre,-1);

    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;

            if (edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if (!vis[v]) {
                    vis[v] = true;
                    q.push(v);

                }

            }

        }

    }
    if (pre[t] == -1)return false;
    else return true;

}
//返回的是最大流,cost 存的是最小费用
int minCostMaxflow(int s, int t, int &cost) {
    int flow = 0;
    cost = 0;
    while (spfa(s, t)) {
        int Min = INF;
        for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
            if (Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;

        }
        for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
            edge[i].flow += Min;
            edge[i ^ 1].flow -= Min;
            cost += edge[i].cost * Min;

        }
        flow += Min;

    }
    return flow;

}
int main(){
    cin>>n>>q;
    init();
    int S=0,T=2*n+1;
    rep(i,1,n){
        addv(S,i,1,0);
        r[i]=n;
        l[i]=1;
        rep(j,1,n){
            addv(i+n,T,1,2*j-1);
        }
    }
    int flag=1;
    while(q--){
        int op,x,y,v;
        scanf("%d%d%d%d",&op,&x,&y,&v);
        if(op==1){
            rep(i,x,y){
                l[i]=max(l[i],v);
            }
        }else{
            rep(i,x,y){
                r[i]=min(r[i],v);
            }
        }
    }
    rep(i,1,n){
        if(l[i]>r[i]){
            flag=0;
            break;
        }
        rep(j,l[i],r[i]){
            addv(i,j+n,1,0);
        }
    }
    if(flag==0){
        puts("-1");
        return 0;
    }
    int c;
    minCostMaxflow(S,T,c);
    cout<<c<<endl;
    
} 

 

posted @ 2019-09-29 20:02  光芒万丈小太阳  阅读(278)  评论(0编辑  收藏  举报