gym102394 2019CCPC哈尔滨 A. Artful Paintings(二分 差分约束 优化)

linkkk

题意:

n n n个方块, m 1 m1 m1个限制条件 1 1 1 m 2 m2 m2个限制条件 2 2 2,限制条件的描述如下:
1 : l , r , k 1:l,r,k 1:l,r,k表示区间 [ l , r ] [l,r] [l,r]染色的方块数至少 k k k
2 : l , r , k 2:l,r,k 2:l,r,k表示除区间 [ l , r ] [l,r] [l,r]染色的方块数至少 k k k

思路:

如果只有条件 1 1 1的话就是 A B C 216 ABC216 ABC216 G G G了(传送门
当时有贪心跟差分约束两种解法,但是加了条件 2 2 2的话,就只能差分约束了。
先把条件都列出来,设 s u m i sum_i sumi表示 [ 1 , i ] [1,i] [1,i]中染色的方块数。
对于条件 1 1 1的约束有:

  • s u m r − s u m l − 1 > = k sum_r-sum_{l-1}>=k sumrsuml1>=k

对于条件 2 2 2的约束有:

  • s u m l − 1 − s u m 0 + s u m n − s u m r > = k sum_{l-1}-sum_0+sum_n-sum_r>=k suml1sum0+sumnsumr>=k
  • 移项变成了 s u m l − 1 − s u m r > = k − s u m n sum_{l-1}-sum_r>=k-sum_n suml1sumr>=ksumn
  • 由于这个式子有三个变量,可以考虑二分 s u m n sum_n sumn,即二分染色的总方块数。
  • 因为每个限制条件都是至少为 k k k,所以答案是具有单调性的,如果说染色 x x x个可以,染色 x + 1 x+1 x+1个也一定满足条件。

还有一些合理性的约束:

  • 0 < = s u m i − s u m i − 1 < = 1 0<=sum_i-sum_{i-1}<=1 0<=sumisumi1<=1
  • m i d < = s u m n − s u m 0 < = m i d mid<=sum_n-sum_0<=mid mid<=sumnsum0<=mid,这个是为了保证 s u m n − s u m 0 = = m i d sum_n-sum_0==mid sumnsum0==mid的。

建图,想跑最短路的话,松弛条件为 d i s v < = d i s u + w dis_v<=dis_u+w disv<=disu+w,建边从 u − > v u->v u>v,权值为 w w w,也就是 d i s v − d i s u < = w dis_v-dis_u<=w disvdisu<=w,后者向前者连边。

具体做法,二分染色的总方块数,每次 c h e c k check check的时候,建图跑最短路,用 s p f a spfa spfa判断有没有负环。
这样可能还是会TLE,加一个判断负环的小优化:如果在最短路过程中 d i s i < 0 dis_i<0 disi<0就一定存在负环,因为在上面的连边过程中,从 i − > i − 1 i->i-1 i>i1有一条权值为 0 0 0的边了。
最长路解法博客

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 2e5 + 6;
#define debug(x) cout << #x << ":" << x << endl;
#define mst(x, a) memset(x, a, sizeof(x))
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)


int n,m1,m2,S,T;
struct Node{
	int l,r,k,op;
};
vector<Node>Q;
vector< pair<int,int> >e[maxn];
int vis[maxn],d[maxn],dist[maxn];
int  spfa(int S)
{
	int flag=0;
	queue<int>q;
	q.push(S);
	while(q.size())
	{
		int fr = q.front();
		q.pop();
		vis[fr]  =0 ;
		if(flag) break;
		for(auto frr:e[fr])
		{
			int v = frr.first;
			int w = frr.second;
			if(dist[v]>dist[fr]+w)
			{
				dist[v] = dist[fr] + w;
				if(vis[v]==0) vis[v]  = 1,q.push(v),d[v]++;
				if(d[v]>n+1) 
				{
					flag=1;
					break;
				}
				if(dist[v]<0) return 1;
			}
		}
	}
	return flag;
}
int ok(int mid)
{
	rep(i,0,n) e[i].clear(),vis[i] =0 ,d[i] =0 ,dist[i] =1e9 ;
	dist[0] =0 ;vis[0]=1;d[0]=1;
	rep(i,1,n)
	{
		e[i-1].push_back({i,1});
		e[i].push_back({i-1,0});
	}
	for(Node fr:Q)
	{
		int l = fr.l;
		int r = fr.r;
		int op = fr.op;
		int k = fr.k;
		if(k>mid) return 0;
		if(op==1)
		{
			e[r].push_back({l-1,-k});
		}
		else
		{
			int T = k - mid;
			e[l-1].push_back({r,-T});
		}
	}
	e[0].push_back({n,mid});
	e[n].push_back({0,-mid});
	return (!spfa(S));
}
int main() {

    int _;scanf("%d",&_);
    while (_--) {
    	scanf("%d",&n);
    		scanf("%d",&m1);
    			scanf("%d",&m2);
    			Q.clear();
    	S =  0;
    //	T = n + 4;
    	for(int i=1 ;i<=m1 ;i++)
    	{
    		int l,r,k;scanf("%d%d%d",&l,&r,&k);
    		Q.push_back({l,r,k,1});
    	}
    	for(int i=1 ;i<=m2 ;i++)
    	{
    		int l,r,k;scanf("%d%d%d",&l,&r,&k);
    		Q.push_back({l,r,k,2});
    	}
  //  cout<<"##"<<endl;
  		//for(int i=0;i<=n;i++) cout<<ok(i)<<endl;
        int L = 0,R = n ,ans=-1;
    	while(L<=R)
    	{
    		int mid = (L+R)>>1;
    		if(ok(mid)) ans = mid,R = mid- 1;
    		else L = mid + 1;
    	} 

    	printf("%d\n",ans);
        
    }
    return 0;
}
/*

*/

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值