P6967 [NEERC2016]Delight for a Cat

题解

考虑线性规划(我明明还不会。。。)

\[\sum_{i=1}^{n-k+1}[\sum_{j=i}^{i+k-1}x_j\le m_1]=n-k+1\\ \sum_{i=1}^{n-k+1}[\sum_{j=i}^{i+k-1}x_j\ge m_2]=n-k+1\\ \]

考虑将小于等于变成等于

\[\sum_{i=1}^{n-k+1}[\sum_{j=i}^{i+k-1}x_j+y_i=m_1]=n-k+1\\ \sum_{i=1}^{n-k+1}[\sum_{j=i}^{i+k-1}x_j=m_2+z_i]=n-k+1\\ \]

我们考虑网络流,但是网络流有一个限制就是变量最多只能在等式的两边各出现一次——因为在网络流中,点代表的是等式,而每一条边才能是变量(即流量),众所周知,一条边只能连两个点,一个出点一个入点,即只能出现在等式的两边各一次。

我们考虑将上面的式子展开,再做一个循环的差分,你会发现他们是等价的。

\[0=0\\ \sum_{i=1}^k x_i+y_1=m_1\\ \sum_{i=1}^k x_i=m_2+z_1\\ \sum_{i=2}^{k+1} x_i+y_2=m_1\\ \sum_{i=2}^{k+1} x_i=m_2+z_2\\ ...\\ \sum_{i=n-k+1}^n x_i+y_{n-k+1}=m_1\\ \sum_{i=n-k+1}^n x_i=m_2+z_{n-k+1} \]

对于做完差分的式子(下方),你可以发现,每一个变量只在等式的两边各出现了一次,所以就可以直接跑网络流了,对于常量,你就让他直接向源汇点连边即可,而对于 \(y_i\)\(z_i\) ,由于他们是只需要满足时正的就可以了,他们时连接一条 \(INF\) 边,而对于 \(x_i\) ,只能取 \(0\)\(1\) ,你就赋流量为 \(1\) 即可。

\[\sum_{i=1}^k x_i+y_1=m_1\\ m_1=y_1+z_1+m_2\\ x_{k+1}+y_2+z_1+m_2=x_1+m_1\\ m_1=y_2+z_2+m_2\\ ......\\ x_n+y_{n-k+1}+z_{n-k}+m_2=x_{n-k}+m_1\\ m_1=y_{n-k+1}+z_{n-k+1}+m_2\\ z_{n-k+1}+m_2=\sum_{i=n-k+1}^n x_i \]

费用的话相信你们自己都会搞,然后跑一个最大费用最大流就行了吧。

代码如下

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+5;
const int INF=1e18;
int n,k,m1,m2;
int cost[2][N],tag[N];
int from,to,id[2][N],tot=0;
struct Edge{int nxt,to,flow,cost;};vector<Edge> e;int fir[N<<1];
int add(int u,int v,int x,int y){e.push_back((Edge){fir[u],v,x,y});return fir[u]=e.size()-1;}
queue<int> q;bool vis[N<<1];
int dis[N<<1],cur[N<<1],res=0;
bool spfa(){
	for(int i=1;i<=tot;++i) dis[i]=-INF,cur[i]=fir[i];
	dis[from]=0,vis[from]=true,q.push(from);
	while(!q.empty()){
		int u=q.front();vis[u]=false,q.pop();
		for(int i=fir[u];i>=0;i=e[i].nxt){
			int v=e[i].to;
			if(!e[i].flow||dis[v]>=dis[u]+e[i].cost) continue;
			dis[v]=dis[u]+e[i].cost;
			if(!vis[v]) vis[v]=true,q.push(v);
		}
	}
	return dis[to]!=-INF;
}
int dfs(int u,int flow){
	if(u==to) return flow;
	int res=0;vis[u]=true;
	for(int i=cur[u];i>=0&&flow;i=e[i].nxt){
		int v=e[i].to;cur[u]=i;
		if(!e[i].flow||dis[v]!=dis[u]+e[i].cost||vis[v]) continue;
		int tmp=dfs(v,min(flow,e[i].flow));
		e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
	}
	return vis[u]=false,res;
}
signed main(){
	cin>>n>>k>>m1>>m2,m1=k-m1;
	for(int i=1;i<=n;++i) scanf("%lld",&cost[0][i]),res+=cost[0][i];
	for(int i=1;i<=n;++i) scanf("%lld",&cost[1][i]);
	from=++tot,to=++tot;
	memset(fir,-1,sizeof(fir));
	for(int i=1;i<=n-k+2;++i){
		id[0][i]=++tot;
		if(i!=n-k+2){
			add(from,id[0][i],m1,0);
			add(id[0][i],from,0,0);
		}
		if(i!=1){
			add(id[0][i],to,m2,0);
			add(to,id[0][i],0,0);
		}
	}
	for(int i=1;i<=n-k+1;++i){
		id[1][i]=++tot;
		add(from,id[1][i],m2,0);
		add(id[1][i],from,0,0);
		add(id[1][i],to,m1,0);
		add(to,id[1][i],0,0);
	}
	for(int i=1;i<=n;++i){
		int tmp1,tmp2;
		if(i<=k) tmp1=id[0][1];
		else tmp1=id[0][i-k+1];
		if(i>=n-k+1) tmp2=id[0][n-k+2];
		else tmp2=id[0][i+1];
		tag[i]=add(tmp1,tmp2,1,cost[1][i]-cost[0][i]);
		add(tmp2,tmp1,0,cost[0][i]-cost[1][i]);
	}
	for(int i=1;i<=n-k+1;++i){
		add(id[0][i],id[1][i],INF,0);
		add(id[1][i],id[0][i],0,0);
	}
	for(int i=2;i<=n-k+2;++i){
		add(id[0][i],id[1][i-1],INF,0);
		add(id[1][i-1],id[0][i],0,0);
	}
	while(spfa()) res+=dfs(from,INF)*dis[to];
	printf("%lld\n",res);
	for(int i=1;i<=n;++i){
		if(e[tag[i]].flow) printf("S");
		else printf("E");
	}
	printf("\n");
	return 0;
}
posted @ 2021-01-05 18:23  Point_King  阅读(52)  评论(0编辑  收藏  举报