题意:
有
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 sumr−suml−1>=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 suml−1−sum0+sumn−sumr>=k
- 移项变成了 s u m l − 1 − s u m r > = k − s u m n sum_{l-1}-sum_r>=k-sum_n suml−1−sumr>=k−sumn
- 由于这个式子有三个变量,可以考虑二分 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<=sumi−sumi−1<=1
- m i d < = s u m n − s u m 0 < = m i d mid<=sum_n-sum_0<=mid mid<=sumn−sum0<=mid,这个是为了保证 s u m n − s u m 0 = = m i d sum_n-sum_0==mid sumn−sum0==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 disv−disu<=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−>i−1有一条权值为
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;
}
/*
*/