Luogu P1993 题解

p1993 小康的农场

CSP_S 1=之后就没怎么写题解。。

推荐博客食用

预备知识

明显这是一道差分约束的题,以下简称差分

有些人可能不了解差分,请点 [传送门]

至于用差分做的题的特征,无一都是你可以列出每个变量之间的一些不等关系,就如下
\[ \begin{cases} x_1-x_2 \le a_1 \\ x_2-x_3 \le a_2 \\ x_3-x_4 \le a_3 \\ \ldots \ldots \\ x_n-x_n \le a_{n-1} \\ \end{cases} \]

对于这样的题我们把他转化为一个图来解决

具体实现是:

  1. 对于一个不等关系,把它转化为形如\(x-y \le c\)的式子
  2. \(y\)\(x\)连一条权为c的边
  3. 若是‘=’ 号,转化为\(x-y \le c\)\(y-x \le c\),来处理
  4. 构建一个\(0\)节点,对每个节点的边权都为\(0\),防止图不连通,而且这样不会影响最短路
  5. 跑一次最短路,因为有负权用spfa

大体的步骤,就是这些了

重点

但是,这道题显得奇奇怪怪,为啥呢,用朴素的SPFA跑出TLE了,看题解都是用的dfs优化,可我偏不,事实上是不想删代码,我加了容错SLFmcfx,就轻松过掉了,时间优化巨大

SPFA死了吗,没有,因为就连p4779,也没有卡住我

值得一提的是,你的容错值定义的要靠近零一点,在\(-3\)\(0\),之间为能过掉,因为这个题有负权,如果全是正权的话在\(0\)\(4\) ,比较合适

还有一点就是,必须要有mcfx,区间的跨度在1000左右为适宜,为\(200\)\(1000\),最好,单独的优化价值都不大,可是加在一起,就是巨大的飞跃

事实上,是因为我不会dfs的spfa,蒟蒻的无力。。。

哈哈

AC代码

有详细注释

#include 
#include 
#include 
#include 
using namespace std;
const int Maxn=1e4+11;
struct Node {
    int to,lac,wg;
}edge[Maxn*3];
int n,m,t,a,b,c,h[Maxn],cnt,dis[Maxn],tot[Maxn];
bool vis[Maxn];
void insert(int x,int y,int c){//邻接表存边 
    edge[cnt].lac=h[x];
    edge[cnt].to=y;
    edge[cnt].wg=c;
    h[x]=cnt++;//我的边编号为从0开始,为了
    //找对应边方便  ^1,即可 
}
int val=-1,l=102,r=1138;//玄学取值 
bool spfa(){
    memset(dis,63,sizeof dis);//刚开始给个最大值 
    dis[0]=0;vis[0]=1;tot[0]=1;
    deque q;
    q.push_front(0);//0入队 
    while(!q.empty()){
        int fr=*(q.begin()); 
        q.pop_front();
        vis[fr]=0;
        for(int i=h[fr];i!=-1;i=edge[i].lac){
            int to=edge[i].to;
            if(dis[to]>dis[fr]+edge[i].wg){//能更新则更新 
                dis[to]=dis[fr]+edge[i].wg;
                if(!vis[to]){//不在队里考虑将他入队 
                    vis[to]=1;
                    if(tot[to]>=l&&tot[to]<=r)  q.push_front(to);//mcfx优化 
                    else if(q.size()==0) q.push_back(to);//队里没有东西,直接进队 
                    else if(dis[to]>dis[*(q.begin())]+val) q.push_back(to);//容错SLF优化 
                    else q.push_front(to);
                    tot[to]++;//别忘记录入队次数 
                    if(tot[to]==n) return false;//每个节点入队达到n次就无解 
                }
            }
        }
    }
    return true;
}
int main() {
//  freopen("kk.in","r",stdin);//文件操作,因为我这种蒟蒻经常错 
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);//初始化head,不能忘!
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&t,&a,&b);
        if(t==3){
            insert(a,b,0);
            insert(b,a,0);//对于等于的情况特殊处理 
            continue;
        }
        scanf("%d",&c);
        if(t==1) insert(a,b,-c);//寻常的连边 
        if(t==2) insert(b,a,c);// 
    }
    for(int i=1;i<=n;i++) insert(0,i,0);//万一不连通的话,每一个联通的区域都有可能有负权圈 
    if(spfa()) printf("Yes");//bool型 spfa 
    else printf("No");
    return 0;
}

这样子,小\(f\)再也 不用b担心自己的\(spfa\)被卡了

你可能感兴趣的:(Luogu P1993 题解)