2019-2020 ICPC, Asia Jakarta Regional Contest E - Songwriter 差分约束(随机化优化|栈优化)

最短路三角形不等式:Xi<=Xj+w(根据最短路的定义,要是不满足的话就不是最短路了)

给出若干个形如Xi-Xj<=w的约束条件,考虑求一组合法的解。

把问题转化成求最短路,对于Xi-Xj<=w,我们从j向i连一条边权为w的边

如果有负环的话即无解。

...?

但差分约束难搞一点的话还要求最大解、最小解,以及其他更多的满足条件。

1.先看看求最大解。

可能有点反直觉,但是:通过最短路径算出来的未知数都能到最大值

粗略证明:假设M1,M2,M3..是一组合法的最大解,根据Bellman-Ford的过程,一开始的路径都初始化为无穷大,然后检查所有三角形不等式,不满足最短路三角形不等式则更新。最后求出来的dis数组就是到每个点的最短路长度。

那我们初始化为无穷大时,求出来的是D1,D2,D3...初始化为M1,M2,M3时,求出来的就是M1,M2,M3..(刚才已经假设这组M是合法的解,不再更新)。那么D1,D2,D3..==M1,M2,M3..因为同一个算法,同一个过程,总不能因为初始值设置大了,答案反而小了吧?

所以求最大解时跑最短路,且不等式的形式都要形如Xi-Xj<=w,然后从j向i连边权为w的边。

一些常见的最短路变形:

Xi>Xj ,  Xi>=Xj+1, Xj-Xi<=-1

Xi-Xj>=k,  Xj-Xi<=-k

Xi==Xj,拆成Xi>=Xj,Xj>=Xi

Xi-Xj==k,拆成Xi-Xj>=k,Xi-Xj<=k,再对第一个进行变形得Xj-Xi<=-k

更新时的注意事项:

初始化为无穷大,用最短路三角形不等式更新:

if(dis[v]>dis[u]+e[i].w)
   dis[v]=dis[u]+e[i].w;

2.同理的,求解最小值时等价为跑最长路

最长路满足的三角形不等式:Xi-Xj>=w

不等式都要变形成形如Xi-Xj>=w的形式,从j向i连边权为w的边

初始化为无穷小,注意更新时符号也要变化

if(dis[v]<dis[u]+w)
   dis[v]=dis[u]+w;

3.玄学的优化

你应该知道,关于spfa,它死了的这件事..

今天一道构造题写了差分约束,t25,赛后学长说换成双端队列随机取队头队首,或者把queue改成stack可能会有奇效

测了一下随机化是过了,栈没过。不是很明白原理,可能是根据卡spfa的数据出的?

4.此题的代码:

//stack ,    T25
#include<bits/stdc++.h>
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int tot,n,m,l,r,k;
int a[maxn];
struct lys{
    int from,to,nex;
    ll val;
}e[int(1e6)];
int head[int(1e6)];
void add(int from,int to,ll val){
    tot++;e[tot].from=from;e[tot].to=to;
    e[tot].val=val;e[tot].nex=head[from];head[from]=tot;
}
int cnt[maxn];
bool vis[maxn];
ll dis[maxn];
stack<int>s;
bool spfa(void)
{
    for(int i=1;i<=n;i++)   dis[i]=LONG_LONG_MIN;
    dis[0]=0;
    vis[0]=true;
    s.push(0);
    while(!s.empty())
    {
        int u;
        u=s.top();s.pop();
        vis[u]=false;
        for(int i=head[u];i;i=e[i].nex)
        {
            int v=e[i].to;ll w=e[i].val;
            if(dis[v]<dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    vis[v]=true;
                    cnt[v]++;
                    if(cnt[v]>=n+1) return false;
                    s.push(v);
                }
            }
        }
    }
    return true;
}
int main()
{
    fastio;
    cin>>n>>l>>r>>k;
    for(int i=1;i<=n;i++)  cin>>a[i];
    for(int i=1;i<n;i++){
        if(a[i]==a[i+1]){
            add(i,i+1,0);
            add(i+1,i,0);
        }
        else if(a[i+1]>a[i]){
            // i+1 <= i + k
            add(i+1,i,-k);
            // i+1 >= i + 1   -> i <= i+1 -1
            add(i,i+1,1);
        }
        else {
            add(i+1,i,1);
            add(i,i+1,-k);
        }
    }
    for(int i=1;i<=n;i++){
        add(0,i,l);
    }
    if(!spfa())
    {
        cout<<-1<<endl;
        return 0; 
    }
    else {
        bool valid=true;
        for(int i=1;i<=n;i++){
            if(dis[i]>r) valid=false;
        }
        if(!valid){
            cout<<-1;
        }
        else {
            for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
        }
    }
    return 0;
} 

加了随机优化的双端队列,accepte

#include<bits/stdc++.h>
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int tot,n,m,l,r,k;
int a[maxn];
struct lys{
    int from,to,nex;
    ll val;
}e[int(1e6)];
int head[int(1e6)];
void add(int from,int to,ll val){
    tot++;e[tot].from=from;e[tot].to=to;
    e[tot].val=val;e[tot].nex=head[from];head[from]=tot;
}
int cnt[maxn];
bool vis[maxn];
ll dis[maxn];
bool spfa(void)
{
    for(int i=1;i<=n;i++)   dis[i]=LONG_LONG_MIN;
    dis[0]=0;
    deque<int> q;
    q.push_front(0);
    vis[0]=true;
    while(!q.empty())
    {
        int u;
        if(rand()&1)u=q.front(),q.pop_front();
        else u=q.back(),q.pop_back();
        vis[u]=false;
        for(int i=head[u];i;i=e[i].nex)
        {
            int v=e[i].to;ll w=e[i].val;
            if(dis[v]<dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    vis[v]=true;
                    cnt[v]++;
                    if(cnt[v]>=n+1) return false;
                     q.push_front(v);
                }
            }
        }
    }
    return true;
}

int main()
{
    fastio;
    cin>>n>>l>>r>>k;
    for(int i=1;i<=n;i++)  cin>>a[i];
    for(int i=1;i<n;i++){
        if(a[i]==a[i+1]){
            add(i,i+1,0);
            add(i+1,i,0);
        }
        else if(a[i+1]>a[i]){
            // i+1 <= i + k
            add(i+1,i,-k);
            // i+1 >= i + 1   -> i <= i+1 -1
            add(i,i+1,1);
        }
        else {
            add(i+1,i,1);
            add(i,i+1,-k);
        }
    }
    for(int i=1;i<=n;i++){
        add(0,i,l);
    }
    if(!spfa())
    {
        cout<<-1<<endl;
        return 0; 
    }
    else {
        bool valid=true;
        for(int i=1;i<=n;i++){
            if(dis[i]>r) valid=false;
        }
        if(!valid){
            cout<<-1;
        }
        else {
            for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
        }
    }
    return 0;
} 

 

posted @ 2023-02-10 00:08  liyishui  阅读(29)  评论(0编辑  收藏  举报