0
点赞
收藏
分享

微信扫一扫

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


​​linkkk​​

题意:

个方块,个限制条件个限制条件,限制条件的描述如下:
表示区间染色的方块数至少
表示除区间染色的方块数至少

思路:

如果只有条件的话就是了(​​​传送门​​​)
当时有贪心跟差分约束两种解法,但是加了条件的话,就只能差分约束了。
先把条件都列出来,设表示中染色的方块数。
对于条件的约束有:

对于条件的约束有:

  • 移项变成了
  • 由于这个式子有三个变量,可以考虑二分,即二分染色的总方块数。
  • 因为每个限制条件都是至少为,所以答案是具有单调性的,如果说染色个可以,染色个也一定满足条件。

还有一些合理性的约束:

  • ,这个是为了保证的。

建图,想跑最短路的话,松弛条件为,建边从,权值为,也就是,后者向前者连边。

具体做法,二分染色的总方块数,每次的时候,建图跑最短路,用判断有没有负环。
这样可能还是会TLE,加一个判断负环的小优化:如果在最短路过程中就一定存在负环,因为在上面的连边过程中,从有一条权值为的边了。
​​​最长路解法博客​​

代码:

#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;
}
/*

*/


举报

相关推荐

0 条评论