zoukankan      html  css  js  c++  java
  • P6967 [NEERC2016]Delight for a Cat

    题解

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

    [sum_{i=1}^{n-k+1}[sum_{j=i}^{i+k-1}x_jle m_1]=n-k+1\ sum_{i=1}^{n-k+1}[sum_{j=i}^{i+k-1}x_jge 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
    ",res);
    	for(int i=1;i<=n;++i){
    		if(e[tag[i]].flow) printf("S");
    		else printf("E");
    	}
    	printf("
    ");
    	return 0;
    }
    
  • 相关阅读:
    vue使用talkIngData统计
    vue项目中使用百度统计
    vue的指令修饰符
    提问:
    整理心情再投入下一个阶段
    CSS写三角形
    单行文本和多行文本超出隐藏
    清除浮动的方法
    用JS表示斐波拉契数列
    vue中使用动态挂载和懒加载,实现点击导航栏菜单弹出不同弹框
  • 原文地址:https://www.cnblogs.com/Point-King/p/14237380.html
Copyright © 2011-2022 走看看