Logo __vector__ 的博客

博客

题解:CF863F Almost Permutation

...
__vector__
2025-12-01 12:56:02

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-12-31 14:04:28

vp 被卡死在这题了,写个题解记录一下。

题意

长度为 $n$ 的序列,第 $i$ 个数的值域范围是 $l_i,r_i$。

求所有可能的序列中,所有数出现次数平方和的最小可能的值。

做法

注意到每个位置 $i$ 都只能有一个数,并且这个数在 $[l_i,r_i]$ 之间,相当于位置 $i$ 可以为 $[l_i,r_i]$ 中的某个数贡献一次出现次数。

从这里可以想到网络流,由于最终每个数都要出现一次,考虑将原问题求的总代价作为费用,而最大流量设计为能有效安排的位置个数,这样保证了使用最小费用最大流可以在有解的情况下求出最小代价,当然,此时最大流是 $n$。

考虑一下发现,上述过程对应的是源点向每个位置 $i$ 连一条容量为 $1$,代价为 $0$ 的边,每个位置 $i$ 向数值 $[l_i,r_i]$ 连一条容量为 $1$,代价为 $0$ 的边。

由于每种数值 $j$ 的出现次数决定了最终代价,考虑每个数值 $j$ 向汇点连 $n$ 条容量为 $1$,费用分别为 $1^2-0^0,2^2-1^2,3^2-2^2,\cdots,n^2-(n-1)^2$ 的边。

然后跑最小费用最大流就行,我用的是 Dinic,用时 46ms。

#include <bits\/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
#define mkpr make_pair
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
template<class T>
void ckmx(T& a,T b){
    a=max(a,b);
}
template<class T>
void ckmn(T& a,T b){
    a=min(a,b);
}
template<class T>
T gcd(T a,T b){
    return !b?a:gcd(b,a%b);
}
template<class T>
T lcm(T a,T b){
    return a\/gcd(a,b)*b;
}
#define gc getchar()
#define eb emplace_back
#define pc putchar
#define ep empty()
#define fi first
#define se second
#define pln pc('\n');
template<class T>
void wrint(T x){
    if(x<0){
        x=-x;
        pc('-');
    }
    if(x>=10){
        wrint(x\/10);
    }
    pc(x%10^48);
}
template<class T>
void wrintln(T x){
    wrint(x);
    pln
}
template<class T>
void read(T& x){
    x=0;
    int f=1;
    char ch=gc;
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=gc;
    }
    while(isdigit(ch)){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=gc;
    }
    x*=f;
}
const int maxn=55;
int n,q;
int mx[maxn],mn[maxn];
struct EDGE{
    int to,nxt;
    int lim,cost;
}edge[maxn*maxn*6];
int head[maxn*2];
int cnt=1;
void _add(int u,int to,int lim,int cost){
    edge[++cnt].to=to;
    edge[cnt].nxt=head[u];
    edge[cnt].lim=lim;
    edge[cnt].cost=cost;
    head[u]=cnt;
}
void add(int u,int to,int lim,int cost){
	\/\/printf("%d -> %d\n",u,to);
    _add(u,to,lim,cost);
    _add(to,u,0,-cost);
}
int source=0,receive;
int dis[maxn*2];
int cur[maxn*2];
bool ins[maxn*2];
bool vis[maxn*2];
bool bfs(){
    queue<int> q;
    memset(dis,0x3f,sizeof dis);
    memcpy(cur,head,sizeof head);
	memset(vis,0,sizeof vis);
    q.push(source);
    dis[source]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        ins[u]=0;
        for(int i=head[u];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(edge[i].lim&&dis[to]>dis[u]+edge[i].cost){
                dis[to]=dis[u]+edge[i].cost;
                if(!ins[to]){
                    q.push(to),ins[to]=1;
                }
            }
        }
    }
    return (dis[receive]<=1e8);
}
int dfs(int u,int flow){
    if(!flow||u==receive)return flow;
    int used=0;
	vis[u]=1;
    for(int& i=cur[u];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(dis[to]==dis[u]+edge[i].cost&&!vis[to]&&edge[i].lim>0){
            int use=dfs(to,min(flow-used,edge[i].lim));
            edge[i].lim-=use;
            edge[i^1].lim+=use;
            used+=use;
        }
        
        if(used==flow)return used;
    }
    return used;
}
void Dinic(){
    int mxflow=0,totcost=0;
    while(bfs()){
        int nwflow=dfs(source,1e9);
        mxflow+=nwflow;
        totcost+=nwflow*dis[receive];
    }
    if(mxflow!=n){
        puts("-1");
    }else{
        printf("%d\n",totcost);
    }
}
void solve(int id_of_test){
	read(n);
    read(q);
    FOR(i,1,n){
        mn[i]=1,mx[i]=n;
    }
    receive=2*n+1;
    FOR(i,1,q){
        int t,l,r,v;
        read(t);
        read(l);
        read(r);
        read(v);
        if(t==1){
            FOR(j,l,r){
                ckmx(mn[j],v);
            }
        }else{
            FOR(j,l,r){
                ckmn(mx[j],v);
            }
        }
    }
    FOR(i,1,n){
        add(source,i,1,0);
    }
    FOR(i,1,n){
        FOR(j,mn[i],mx[i]){
            add(i,n+j,1,0);
        }
    }
    FOR(v,1,n){
        FOR(mult,1,n){
            add(v+n,receive,1,mult*mult-(mult-1)*(mult-1));
        }
    }
    Dinic();
}
int main()
{
	int T;
	T=1;
	FOR(_,1,T){
		solve(_);
	}
	return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。