[2019CCPC哈尔滨] A Artful Paintings 二分+差分约束

N ≤ 3 e 3 N\leq3e3 N3e3个格子,你可以任意给每个格子染色,但是要满足 M ≤ 3 e 3 M\leq3e3 M3e3限制条件,限制条件有两种类型:
1. 1. 1. 区间 [ l , r ] [l,r] [l,r]中被染色的格子数量不少于 K K K
2. 2. 2. 区间 [ l , r ] [l,r] [l,r]外被染色的格子数量不少于 K K K
在满足所有限制条件下求染色格子数量的最小值。
显然染色格子数量越多,越容易满足。因此二分染色格子数 m i d mid mid。然后考虑染色数的前缀和 S i S_{i} Si
问题转化为对于每个二分的 m i d mid mid,都有:
1. S l − 1 − S r ≤ − k 1. S_{l-1}-S_{r}\leq-k 1.Sl1Srk
2. S r − S l − 1 ≤ m i d − k 2. S_{r}-S_{l-1}\leq mid-k 2.SrSl1midk
3. S i − S i − 1 ≤ 1 3. S_{i}-S_{i-1}\leq 1 3.SiSi11
4. S i − 1 − S i ≤ 0 4. S_{i-1}-S_{i}\leq0 4.Si1Si0
5. S n − S 0 ≤ m i d 5. S_{n}-S_{0}\leq mid 5.SnS0mid
6. S 0 − S n ≤ m i d 6. S_{0}-S_{n}\leq mid 6.S0Snmid
全部由后者向前者连边,然后判负环,如果不存在负环则说明有解。
直接跑仍然会超时。考虑如果发现某一个点最短路已经为负数,那么必定存在负环,这个时候可以直接退出。因为存在 i i i i − 1 i-1 i1 0 0 0的边。那么先从 0 0 0到那个点然后再返回到 0 0 0一定是负环。

#include
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=3e3+7;
int n,m1,m2; 
struct edge { int v,w; };
struct opt { int l,r,k; }; 
vector<edge> go[N]; 
vector<opt> e1,e2;
int dis[N],cnt[N];
bool inq[N];
bool spfa() {
	queue<int> q;
	while(!q.empty()) q.pop();
	for(int i=0;i<=n;i++) dis[i]=1e9,cnt[i]=0,inq[i]=0;
	dis[0]=0;q.push(0);inq[0]=1,cnt[0]=1; 
	while(!q.empty()) {
		int u=q.front();q.pop();inq[u]=0;
		for(auto &it:go[u]) { int v=it.v,w=it.w;
			if(dis[v]>dis[u]+w) {
				dis[v]=dis[u]+w;
				if(dis[v]<0) return 0; 
				if(!inq[v]) {
					inq[v]=1;
					q.push(v);
					if(++cnt[v]>n) return 0;
				}
			}
		}
	} 
	return 1;
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d%d",&n,&m1,&m2);
		for(int i=0;i<=n;i++) go[i].clear();
		e1.clear();e2.clear(); 
		for(int l,r,k,i=1;i<=m1;i++) {
			scanf("%d%d%d",&l,&r,&k);
			e1.push_back({l,r,k}); 
		} 
		for(int l,r,k,i=1;i<=m2;i++) {
			scanf("%d%d%d",&l,&r,&k);
			e2.push_back({l,r,k}); 
		}
		int l=0,r=n,ans=0;
		while(l<=r) {
			for(int i=0;i<=n;i++) go[i].clear();
			for(int i=1;i<=n;i++) go[i-1].push_back({i,1});
			for(int i=1;i<=n;i++) go[i].push_back({i-1,0});
			int mid=(l+r)>>1;
			go[0].push_back({n,mid});
			go[n].push_back({0,-mid}); 
			for(auto &it:e1) go[it.r].push_back({it.l-1,-it.k});
			for(auto &it:e2) go[it.l-1].push_back({it.r,mid-it.k});
			if(spfa()) ans=mid,r=mid-1;
			else l=mid+1;
		}
		printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(比赛题解)