2019 CCPC 哈尔滨站 A. Artful Paintings 差分约束+二分
Posted valk3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019 CCPC 哈尔滨站 A. Artful Paintings 差分约束+二分相关的知识,希望对你有一定的参考价值。
题意
给出一个长度为 n 的方块区间[1,n],现在要对这个方块区间染色,有两种要求:
- 格式:l, r, k。表示区间[l,r]里至少有 k 个方块被染色
- 格式:l, r, k。表示区间[l,r]之外至少有 k 个方块被染色。
现在给出 n, m1, m2,分别表示有 n 个方块,m1 个 1 类型的要求,m2 个 2 类型的需求。
问最少需要染多少个方块满足所有的要求。
思路
只看要求 1 的话,经典的差分约束。
但是第二个如果列成不等式会有三个变量:(pre[n] - (pre[r]-pre[l-1]) geq k)
化简一下:(pre[l-1] -pre[r] geq k-pre[n])
可以看出 第二个式子中一直存在(pre[n]),那么就想到可不可以先把(pre[n])看做一个定值。
然后就想到了二分。
二分 (pre[n])的值,这样建边之后跑最长路或者最短路,进行差分约束,如果没有负环或者正环,当前(pre[n])的值就可以。
-
最长路建边
对于 m1 个 1 类型的要求:(pre[r]-pre[l-1] geq k)
即 (pre[r] geq pre[l-1] + k)
建一条 (l-1) ——> (r) 权值为 (k) 的边
m2 个 2 类型的要求:(pre[n]-(pre[r]-pre[l-1])geq k)
即 (pre[l-1]>=pre[r] +k-pre[n])
建一条 (r) ——> (l-1) 的权值为 (k-pre[n])的边
注意隐含条件 (pre[i]-pre[i-1]geq 0) ,(pre[i]-pre[i-1]leq 1) 即(pre[i-1]-pre[i]geq-1)
分别为这两个不等式建边:
(i-1) 向 (i) 建立一条权值为 (0) 的边
(i) 向 (i-1) 建立一条权值为 (-1) 的边
因为我们二分 (pre[n])的值,所以还要加上一个条件:
(pre[n]-pre[0]==k),我们把这个条件变成两个不等式:(pre[n]-pre[0]leq k) 即(pre[0]-pre[n]geq -k)
(pre[n]-pre[0]geq k)
再分别建边
-
最短路建边
不等式其实和最长路列出来的一样,只是把(geq) 变成(leq)
比赛的时候写的是最长路疯狂T。
看题解发现,用最长路可以用SLF优化一下SPFA。
如果跑最短路如果遇到 (dis) 值小于 0就可以判断出负环,因为 (i) 到 (i-1) 有一条边权为 0 的边,这样就构成了负环。
首先写了一个最短路的代码,187ms 过了
最短路+提前判环 187ms
/*
* @Autor: valk
* @Date: 2020-08-11 12:38:37
* @LastEditTime: 2020-10-02 20:37:12
* @Description: 如果邪恶 是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
*/
#include <bits/stdc++.h>
#define fuck system("pause")
#define pb emplace_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 3e3 + 10;
struct Edge {
int to, next, val;
} edge[N * 100];
int tot, head[N];
void add(int u, int v, int val)
{
edge[tot] = { v, head[u], val };
head[u] = tot++;
}
struct note {
int l, r, k;
} a[N], b[N];
int dis[N], vis[N], cnt[N];
int n, m1, m2;
int judge(int x)
{
memset(head,-1,sizeof(head));
tot=0;
for(int i=1;i<=n;i++){
add(i,i-1,0);
add(i-1,i,1);
}
for(int i=1;i<=m1;i++){
add(a[i].r,a[i].l-1,-a[i].k);
}
for(int i=1;i<=m2;i++){
add(b[i].l-1,b[i].r,x-b[i].k);
}
add(0,n,x);
add(n,0,-x);
for(int i=0;i<=n;i++){
dis[i]=inf;
vis[i]=cnt[i]=0;
}
dis[0]=0;
queue<int>q;
q.push(0);
vis[0]=cnt[0]=1;
while(!q.empty()){
int u =q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next){
int v =edge[i].to,val=edge[i].val;
if(dis[v]>dis[u]+val){
dis[v]=dis[u]+val;
if(dis[v]<0) return 0;
if(!vis[v]){
if(++cnt[v]>n+1){
return 0;
}
vis[v]=1;
q.push(v);
}
}
}
}
return 1;
}
int main()
{
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m1, &m2);
for (int i = 1; i <= m1; i++) {
scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
}
for (int i = 1; i <= m2; i++) {
scanf("%d%d%d", &b[i].l, &b[i].r, &b[i].k);
}
int l=0,r=n,ans;
while(l<=r){
int mid=(l+r)/2;
if(judge(mid)){
ans=mid;
r=mid-1;
}else{
l=mid+1;
}
}
printf("%d
",ans);
}
return 0;
}
然后写了一个最长路的SLF优化:
这随缘过题。。。
跟假的一样。
最长路+SLF优化 998ms
/*
* @Autor: valk
* @Date: 2020-08-11 12:38:37
* @LastEditTime: 2020-10-03 17:25:30
* @Description: 如果邪恶 是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
*/
#include <bits/stdc++.h>
#define fuck system("pause")
#define pb emplace_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 3e3 + 10;
struct Edge {
int to, next, val;
} edge[N * N];
int tot, head[N];
void add(int u, int v, int val)
{
edge[tot] = { v, head[u], val };
head[u] = tot++;
}
struct note {
int l, r, k;
} a[N], b[N];
int dis[N], vis[N], cnt[N];
int n, m1, m2;
int judge(int x)
{
for (int i = 0; i <= n; i++) {
head[i] = -1;
}
tot = 0;
for (int i = 1; i <= m1; i++) {
add(a[i].l - 1, a[i].r, a[i].k);
}
for (int i = 1; i <= m2; i++) {
add(b[i].r, b[i].l - 1, b[i].k - x);
}
for (int i = 1; i <= n; i++) {
add(i - 1, i, 0);
add(i, i - 1, -1);
}
add(0, n, x);
add(n, 0, -x);
for (int i = 0; i <= n; i++) {
dis[i] = -inf;
vis[i] = cnt[i] = 0;
}
vis[0] = cnt[0] = 1;
dis[0] = 0;
deque<int> q;
q.push_back(0);
while (!q.empty()) {
int u = q.front();
q.pop_front();
vis[u] = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to, val = edge[i].val;
if (dis[v] < dis[u] + edge[i].val) {
dis[v] = dis[u] + edge[i].val;
if (!vis[v]) {
if (++cnt[v] > n + 1)
return 0;
vis[v] = 1;
if (!q.empty() && dis[v] > dis[q.front()]) {
q.push_front(v);
} else {
q.push_back(v);
}
}
}
}
}
return 1;
}
int main()
{
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m1, &m2);
for (int i = 1; i <= m1; i++) {
scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
}
for (int i = 1; i <= m2; i++) {
scanf("%d%d%d", &b[i].l, &b[i].r, &b[i].k);
}
int l = 0, r = n, ans;
while (l <= r) {
int mid = (l + r) / 2;
if (judge(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d
", ans);
}
return 0;
}
今天发现了一个博客也写了SLF优化的最长路,然后用他的代码跑了跑,
只用了93ms
然后我开始了照着代码改代码的提交之路。
各种魔改最后发现竟然是判断负环的地方:
这是我写的判断负环的地方
这是博主写的:
然后我把我写的最长路也改成了这个样子
我的天哪,从998ms 变成了61ms
代码
最长路+SLF优化 +更改判环位置 61ms
/*
* @Autor: valk
* @Date: 2020-08-11 12:38:37
* @LastEditTime: 2020-10-03 17:49:22
* @Description: 如果邪恶 是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
*/
#include <bits/stdc++.h>
#define fuck system("pause")
#define pb emplace_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 3e3 + 10;
struct Edge {
int to, next, val;
} edge[N * N];
int tot, head[N];
void add(int u, int v, int val)
{
edge[tot] = { v, head[u], val };
head[u] = tot++;
}
struct note {
int l, r, k;
} a[N], b[N];
int dis[N], vis[N], cnt[N];
int n, m1, m2;
int judge(int x)
{
for (int i = 0; i <= n; i++) {
head[i] = -1;
}
tot = 0;
for (int i = 1; i <= m1; i++) {
add(a[i].l - 1, a[i].r, a[i].k);
}
for (int i = 1; i <= m2; i++) {
add(b[i].r, b[i].l - 1, b[i].k - x);
}
for (int i = 1; i <= n; i++) {
add(i - 1, i, 0);
add(i, i - 1, -1);
}
add(0, n, x);
add(n, 0, -x);
for (int i = 0; i <= n; i++) {
dis[i] = -inf;
vis[i] = cnt[i] = 0;
}
vis[0] = cnt[0] = 1;
dis[0] = 0;
deque<int> q;
q.push_back(0);
while (!q.empty()) {
int u = q.front();
q.pop_front();
vis[u] = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to, val = edge[i].val;
if (dis[v] < dis[u] + edge[i].val) {
dis[v] = dis[u] + edge[i].val;
cnt[v] = cnt[u] + 1;
if (cnt[v] > n + 1)
return 0;
if (!vis[v]) {
vis[v] = 1;
if (!q.empty() && dis[v] > dis[q.front()]) {
q.push_front(v);
} else {
q.push_back(v);
}
}
}
}
}
return 1;
}
int main()
{
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m1, &m2);
for (int i = 1; i <= m1; i++) {
scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
}
for (int i = 1; i <= m2; i++) {
scanf("%d%d%d", &b[i].l, &b[i].r, &b[i].k);
}
int l = 0, r = n, ans;
while (l <= r) {
int mid = (l + r) / 2;
if (judge(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d
", ans);
}
return 0;
}
/*
10
4 1 1
2 3 1
2 3 2
4 4 0
1 1 1
2 2 1
3 3 1
4 4 1
5 3 0
1 3 2
2 4 1
3 5 2
*/
以上是关于2019 CCPC 哈尔滨站 A. Artful Paintings 差分约束+二分的主要内容,如果未能解决你的问题,请参考以下文章
HDU - 6231 K-th Number (2017CCPC哈尔滨站 二分+尺取法)
2017CCPC-哈尔滨站 Hdu-6230 Palindrome Manacher 主席树