F. Almost Permutation
time limit per test
3 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output
Recently Ivan noticed an array a while debugging his code. Now Ivan can't remember this array, but the bug he was trying to fix didn't go away, so Ivan thinks that the data from this array might help him to reproduce the bug.
Ivan clearly remembers that there were n elements in the array, and each element was not less than 1 and not greater than n. Also he remembers q facts about the array. There are two types of facts that Ivan remembers:
- 1 li ri vi — for each x such that li ≤ x ≤ ri ax ≥ vi;
- 2 li ri vi — for each x such that li ≤ x ≤ ri ax ≤ vi.
Also Ivan thinks that this array was a permutation, but he is not so sure about it. He wants to restore some array that corresponds to the qfacts that he remembers and is very similar to permutation. Formally, Ivan has denoted the cost of array as follows:
, where cnt(i) is the number of occurences of i in the array.
Help Ivan to determine minimum possible cost of the array that corresponds to the facts!
Input
The first line contains two integer numbers n and q (1 ≤ n ≤ 50, 0 ≤ q ≤ 100).
Then q lines follow, each representing a fact about the array. i-th line contains the numbers ti, li, ri and vi for i-th fact (1 ≤ ti ≤ 2, 1 ≤ li ≤ ri ≤ n, 1 ≤ vi ≤ n, ti denotes the type of the fact).
Output
If the facts are controversial and there is no array that corresponds to them, print -1. Otherwise, print minimum possible cost of the array.
Examples
input
Copy
3 0
output
Copy
3
input
Copy
3 1 1 1 3 2
output
Copy
5
input
Copy
3 2 1 1 3 2 2 1 3 2
output
Copy
9
input
Copy
3 2 1 1 3 2 2 1 3 1
output
Copy
-1
题意:每个位置只能填特定的数字,数字有费用,费用为次数的平方,求一个费用最少的填法。
解题思路:费用流裸题。某个位置可以填的数字向这个位置连边。S向所有位置连边,流量为INF,费用为1.所有位置向T连边,流量为1,费用为0.
网上处理费用平方的思路是新建50个节点。这里提供另外一个思路,在费用流跑dfs的时候,动态的修改边的费用即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int MAXN = 7005;
const int INF = 0x3f3f3f3f;
struct edge{
int u,v,cap,cost,next;
}e[MAXN*2];
int head[MAXN],edge_num=0;
void insert_edge(int u,int v,int cap,int cost){
e[edge_num].u=u;
e[edge_num].v=v;
e[edge_num].cap=cap;
e[edge_num].cost=cost;
e[edge_num].next=head[u];
head[u]=edge_num++;
e[edge_num].u=v;
e[edge_num].v=u;
e[edge_num].cap=0;
e[edge_num].cost=-cost;
e[edge_num].next=head[v];
head[v]=edge_num++;
}
int dis[MAXN];
int pre[MAXN*2];
bool vis[MAXN];
bool spfa(int s,int t){
memset(dis,0x3f,sizeof(dis));
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
dis[s]=0;
vis[s]=1;
queue<int> que;
que.push(s);
while(!que.empty()){
int tp=que.front();
que.pop();
vis[tp]=0;
for(int i=head[tp];~i;i=e[i].next){
int v=e[i].v;
int cost=e[i].cost;
if(e[i].cap&&dis[v]>dis[tp]+cost){
dis[v]=dis[tp]+cost;
pre[v]=i;
if(!vis[v]){
vis[v]=1;
que.push(v);
}
}
}
}
if(dis[t]==INF)
return false;
return true;
}
int wm[5006];
bool im[MAXN];//记录需要修改的正向边
bool om[MAXN];//记录需要修改的反向边
pair<int,int> MCMF(int s,int t){
int maxflow=0;
int mincost=0;
int minc;
while(spfa(s,t)){
minc=INF;
int cost=0;
for(int i=pre[t];~i;i=pre[e[i].u])
minc=min(minc,e[i].cap);
for(int i=pre[t];~i;i=pre[e[i].u]){
e[i].cap-=minc;
e[i^1].cap+=minc;
cost+=minc*e[i].cost;//正常计算
if(im[i]){
mincost-=(wm[e[i].cost]-1)*(wm[e[i].cost]-1);//把上次的去掉
e[i].cost=(wm[e[i].cost]+1)*(wm[e[i].cost]+1);//修改边的费用
}
if(om[i]){
mincost+=(wm[-e[i].cost]-1)*(wm[-e[i].cost]-1);//反向边的要注意
e[i].cost=-(wm[-e[i].cost]+1)*(wm[-e[i].cost]+1);
}
}
mincost+=cost;
maxflow+=minc;
}
return {mincost,maxflow};
}
int minn[MAXN];
int maxx[MAXN];
int cnt=0;
int in[MAXN];
int out[MAXN];
int main()
{
for(int j=1;j<5000;j++)
for(int i=1;i<=50;i++)
if(i*i==j)
wm[j]=i;
memset(head,-1,sizeof(head));
edge_num=0;
int N,Q;
scanf("%d%d",&N,&Q);
for(int i=1;i<=N;i++){
minn[i]=1;
maxx[i]=N;
}
int op,l,r,v;
for(int i=0;i<Q;i++){
scanf("%d%d%d%d",&op,&l,&r,&v);
if(op==1){
for(int x=l;x<=r;x++)
minn[x]=max(minn[x],v);
}
else{
for(int x=l;x<=r;x++)
maxx[x]=min(maxx[x],v);
}
}
int S=++cnt;
int T=++cnt;
for(int i=1;i<=N;i++){
in[i]=++cnt;
out[i]=++cnt;
}
for(int i=1;i<=N;i++){
insert_edge(S,in[i],INF,1);
om[edge_num-1]=1;
im[edge_num-2]=1;
insert_edge(out[i],T,1,0);
if(minn[i]>maxx[i]){
cout<<-1<<endl;
return 0;
}
for(int x=minn[i];x<=maxx[i];x++){
insert_edge(in[x],out[i],1,0);
}
}
cout<<MCMF(S,T).first<<endl;
return 0;
}