CF836F 做题笔记
非常好题目,使我原地旋转。
首先数据这么小显然直接暴力求出每个 \(A_i\) 的取值范围。
由于每个 \(A_i\) 只能有一个取值,所以源点先给所有 \(A_i\) 连一个限流为 \(1\) ,费用为 \(0\) 的边。
同时显然还要给每个值域点 (不是 \(A_i\) )向汇点连限为 \(inf\),费用为 \(0\) 的边。(显然多少个 \(A_i\) 取同一个值没有限制)
由于答案有关 \(cnt(i)^2\) ,非常的难处理,所以要用到一个很妙的 Trick 。
考虑先拆点,将 \(n\) 个值域点全部拆成入点和出点,接着对于每个入点向对应的出点连着连 \(n\) 条边,限流都是 \(1\) ,代价依次为 \(1,3,5\ .\ .\ .\ 2n-1\) 。
最后就只要每个 \(A_i\) 向所有可取的值的入点连边即可。
这里的妙处在于 \(1+2+...+(2n-1)=n^2\) ,而每条边限流都是 \(1\) ,那么跑费用流时同一取值就会优先从代价为 \(1,3,5\ .\ .\ .\) 依次流经,最后就可以实现对于颜色 \(i\) 代价正好为 \(cnt(i)^2\)。
code
//writer:Oier_szc
#include <bits/stdc++.h>
//#include <windows.h>
#define TS cerr<<"I AK IOI"<<endl;
#define cr(x) cerr<<x<<endl;
#define cr2(x,y) cerr<<x<<" "<<y<<endl;
#define cr3(x,y,z) cerr<<x<<" "<<y<<" "<<z<<endl;
//#define int long long
using namespace std;
const int N=205,M=2e4+5,INF=2e9,mod=1e9+7;
int n,m,s,t,ans=0;
int l[N],r[N];
int head[N],tot=1;
struct node
{
int ne,to,w,c;
}e[M];
void add(int u,int v,int w,int c)
{
e[++tot]={head[u],v,w,c};
head[u]=tot;
e[++tot]={head[v],u,0,-c};
head[v]=tot;
}
int dis[N],vis[N],incf[N],pre[N];
bool spfa()
{
for(int i=s;i<=t;++i)
{
dis[i]=incf[i]=INF;
vis[i]=false;
pre[i]=-1;
}
queue<int> q;
q.push(s),dis[s]=0;
while(!q.empty())
{
int now=q.front(),to;
q.pop();
vis[now]=false;
for(int i=head[now];i;i=e[i].ne)
{
to=e[i].to;
if(e[i].w&&dis[now]+e[i].c<dis[to])
{
dis[to]=dis[now]+e[i].c;
incf[to]=min(incf[now],e[i].w);
pre[to]=i;
if(!vis[to])
{
vis[to]=true;
q.push(to);
}
}
}
}
return dis[t]!=INF;
}
void MCMF()
{
while(spfa())
{
int ls,x=t;
ans+=incf[t]*dis[t];
while(x!=s)
{
ls=pre[x];
e[ls].w-=incf[t];
e[ls^1].w+=incf[t];
x=e[ls^1].to;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int op,L,R,x;
for(int i=1;i<=n;++i) l[i]=1,r[i]=n;
for(int i=1;i<=m;++i)
{
scanf("%d%d%d%d",&op,&L,&R,&x);
if(op==1)
{
for(int j=L;j<=R;++j) l[j]=max(l[j],x);
}
else if(op==2)
{
for(int j=L;j<=R;++j) r[j]=min(r[j],x);
}
}
s=0,t=3*n+1;
for(int i=1;i<=n;++i)
{
add(i+n+n,t,INF,0);
for(int j=1;j<=n;++j)
{
add(i+n,i+n+n,1,2*j-1);
}
}
for(int i=1;i<=n;++i)
{
if(l[i]>r[i])
{
printf("-1\n");
return 0;
}
add(s,i,1,0);
for(int j=l[i];j<=r[i];++j) add(i,j+n,1,0);
}
MCMF();
printf("%d\n",ans);
return 0;
}