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; }
——愿为泰山而不骄
qq850874665~~