标签:更改 include user else system eps next des math
给出一个长度为 n 的方块区间[1,n],现在要对这个方块区间染色,有两种要求:
现在给出 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 过了
/*
* @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\n",ans);
}
return 0;
}
然后写了一个最长路的SLF优化:
这随缘过题。。。
跟假的一样。
/*
* @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\n", ans);
}
return 0;
}
今天发现了一个博客也写了SLF优化的最长路,然后用他的代码跑了跑,
只用了93ms
然后我开始了照着代码改代码的提交之路。
各种魔改最后发现竟然是判断负环的地方:
这是我写的判断负环的地方
这是博主写的:
然后我把我写的最长路也改成了这个样子
我的天哪,从998ms 变成了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\n", 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 差分约束+二分
标签:更改 include user else system eps next des math
原文地址:https://www.cnblogs.com/valk3/p/13765218.html