2017上海市高校程序设计邀请赛_D

problem list
D 萝莉理论计算机科学家

这里写图片描述

  • 有点恶心,第一感觉是线段树的一定是走火入魔了(比如我)
  • 实际上是差分约束,可以用dfs或并查集来做,并查集不能压缩路径
  • 并查集更艺术些效率更高些~
  • g[x]表示从节点x到根节点的路径上的数值总和(当做向量来理解)
  • c[x]表示从节点x到邻近父节点的数值
  • g[x] = c[x] + g[fx] ;
  • 这里写图片描述
  • c[fy] = g[x] + w - g[y];
  • 差分约束也是在向量计算这里体现出来的
  • 判定矛盾的条件则是当两个端点的根节点一致时
  • 这里写图片描述
  • g[y]-g[x] != w[i] -> ans=0
  • 看数据量知道要离散化处理
  • 还要注意的是题目里讲的是每个节点都是有数值的,数据代指一段节点总和的值 [u,v]==w。实际上如果把节点的值当做木板长度,第u到第v个木板总长是w就不难理解,这里在实际计算过程中要v[i]++或u[i]–,总是要对节点区间增1,就和线段树里面对于连续区间维护的操作一个道理。(这个最容易忽略,很容易漏掉。要是前面的都完成的很好,这里出错以至于全盘重做就很僵硬)
  • 恩,要感谢LCJ,我俩一起商量完善的思路,之后我给实现了下下~
#include <bits/stdc++.h>
using namespace std;
typedef long long           LL ;
typedef unsigned long long ULL ;
const int    maxn = 100000 + 10;
const int    inf  = 0x3f3f3f3f ;
const int    npos = -1         ;
const double eps  = 1e-20      ;

int m, n, ans;
int u[maxn<<1], v[maxn<<1], num[maxn<<1], f[maxn<<1];
LL w[maxn<<1], c[maxn<<1], g[maxn<<1];
std::map< int, int > mp;
int Find(int x){
    int f1=f[x], f2=x;
    if(f1!=f2){
        f2=Find(f1);
    }
    g[x]=c[x]+g[f1];
    return f2;
}
void Union(int x, int fx, int y, int fy, LL z){
    f[fy]=fx;
    c[fy]=g[x]+z-g[y];
}
int main(){
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    while(~scanf("%d",&m)){
        ans=1;
        n=0;
        mp.clear();
        for(int i=1;i<=m;i++){
            scanf("%d %d %lld",&u[i],&v[i],&w[i]);
            v[i]++;
            if(!mp.count(u[i]))
                {mp.insert(make_pair(u[i],1));num[++n]=u[i];}
            if(!mp.count(v[i]))
                {mp.insert(make_pair(v[i],1));num[++n]=v[i];}
        }
        sort(num+1,num+1+n);
        for(int i=1;i<=n;i++){
            f[i]=i;
            g[i]=0;
            c[i]=0;
        }
        for(int i=1;i<=m;i++){
            int x=lower_bound(num+1,num+1+n,u[i])-num;
            int y=lower_bound(num+1,num+1+n,v[i])-num;
            int fx=Find(x), fy=Find(y);
            if(fx==fy){
                if(g[y]-g[x]!=w[i]){
                    ans=0;
                    break;
                }
            }else{
                Union(x,fx,y,fy,w[i]);
            }
        }
        puts(ans?"2333!(=v=)":"666~~~(=_=)");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值