图论1 1009

重量差异

有n个物品,m次操作:告诉物品a比b轻w个重量单位(不会矛盾),询问物品a比物品b轻多少重量单位(根据已有信息)。

对于所有数据,1<=N,M<=100000,1<=a,b<=N

题解

带权并查集模板题。

记录val为x父亲比x重多少,注意路径压缩的合并。

代码有注释。

不要把find(x)写成find(dx)(可能只有我会这么写)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=100005;
int n,m;
int fa[maxn],val[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

int find(int x){
    if(fa[x]==x) return x;
    //w[f]-w[fa[x]]=val[fa[x]]
    //w[fa[x]]-w[x]=val[x]
    //w[f]-w[x]=val[fa[x]]+val[x]
    int f=find(fa[x]);
    val[x]+=val[fa[x]];
    return fa[x]=f;
}

int main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    read(n);read(m);
    for(int i=1;i<=n;i++) fa[i]=i;
    while(m--){
        char op[2];
        scanf("%s",op);
        if(op[0]=='!'){
            int x,y,c;
            read(x);read(y);read(c);
            //w[y]-w[x]=c
            //w[dx]-w[x]=val[x]
            //w[dy]-w[y]=val[y]
            //w[dx]-w[dy]+w[y]-w[x]=val[x]-val[y]
            //w[dx]-w[dy]=val[x]-val[y]-c
            int dx=find(x),dy=find(y);
            if(dx==dy) continue;
            fa[dy]=dx;
            val[dy]=val[x]-val[y]-c;
        }
        else {
            int x,y;
            read(x);read(y);
            int dx=find(x),dy=find(y);
            //w[dx]-w[x]=val[x]
            //w[dx]-w[y]=val[y]
            //w[y]-w[x]=val[x]-val[y]
            if(dx!=dy) printf("UNKNOWN\n");
            else printf("%d\n",val[x]-val[y]);
        }
    }
}
b

车站分级

题目描述

对于100%的数据,n,m<=100000,所有si的和不超过100000。

题解

可以知道他要求的就是一段区间没出现的位置的等级比出现的小,所以从这些位置向出现的位置连边,建出来是个dag,所以跑拓扑找最长链。

但是普通的建边肯定会炸掉,所以考虑优化。

1.建一个虚拟节点,将没出现位置向虚拟点连边,虚拟点向出现的位置连边,这样一次建边是o(n)的。

2.线段树优化建图,考虑没出现的一定是一段一段的区间,我们可以把这些区间拿来向出现的位置连边,但是每个区间都向出现的点连边也不好,再加上上一个优化:建一个虚拟节点连向出现了的位置,所有区间向虚拟节点连边。

然后再跑拓扑。

细节:线段树的叶子节点编号记成区间下标,线段树内向上连边。

一开始找入读为0的点的时候,将他们的d直接赋成1,不管是不是叶子节点,可以考虑因为他建出来了即使没有边连向他,如果他不赋1,那么他管理的位置所得到的就是0,然而实际应该是1.

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=100005;
int n,m,s,a[maxn],du[maxn<<2];
int num,cnt,head[maxn<<2];
int root,ls[maxn<<1],rs[maxn<<1];
struct edge{
    int x,y,val,next;
}e[maxn*40];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

void add(int x,int y,int val){
    e[++cnt]=(edge){x,y,val,head[x]};
    head[x]=cnt;du[y]++;
}
int max(int x,int y){return x>y ? x : y ;}

void build(int &rt,int l,int r){
    if(l==r){
        rt=l;
        return ;
    }
    rt=++num;
    int mid=(l+r)>>1;
    build(ls[rt],l,mid);
    build(rs[rt],mid+1,r);
    add(ls[rt],rt,0);
    add(rs[rt],rt,0);
}

void add_edge(int rt,int l,int r,int a_l,int a_r,int id){
    if(a_l<=l&&r<=a_r){
        add(rt,id,1);
        return ;
    }
    int mid=(l+r)>>1;
    if(a_l<=mid) add_edge(ls[rt],l,mid,a_l,a_r,id);
    if(mid<a_r) add_edge(rs[rt],mid+1,r,a_l,a_r,id);
}

int d[maxn<<2];
void topsort(){
    queue<int> q;
    for(int i=1;i<=num;i++) if(!du[i]) {q.push(i);d[i]=1;}
    int mx=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            du[y]--;d[y]=max(d[y],d[x]+e[i].val);
            if(!du[y]) q.push(y);
        }
    }
    for(int i=1;i<=n;i++) mx=max(mx,d[i]);
    printf("%d",mx);
}

int main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    read(n);read(m);
    num=n;
    build(root,1,n);
    while(m--){
        read(s);
        int now=++num;
        for(int i=1;i<=s;i++) read(a[i]),add(now,a[i],0);
        for(int i=1;i<s;i++) add_edge(root,1,n,a[i]+1,a[i+1]-1,now);
    }
    topsort();
}
c

混合调酒

有k种酒,每种的酒精含量(体积分数)为ai/1000,想喝到n/1000的酒,混合后酒精含量的定义为所有参与混合的鸡尾酒中酒精的体积和除以所有参与混合的鸡尾酒的体积和,问最少需要几杯酒混合可以得到。每种酒无限供应,每杯酒的体积相同。

对于100%的数据,0<=n,a_i<=1000,1<=k<=100000。

题解

假设选出了x杯酒,那么需要满足$\sum_{i=1}^{x}ai=n*x$

再变形得$\sum_{i=1}^{x}(ai-n)=0$

所以将每种酒的浓度减少n,就是选最少的酒使得浓度为0.

考虑$ai-n$的范围是[-1000,1000],在一个数轴上,每一步长度为酒精浓度,就是求多少步回到0.

这就是一个最短路,因为边权一定,所以bfs即可。

1000的范围一定可以算出最优解,因为可以通过组合使得不会跳出去,如果不存在这样的组合,那么他也跳不回来也就无解。

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int maxn=2005;
int n,k;
bool c[maxn];
int d[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

int gcd(int a,int b){return !b ? a : gcd(b,a%b);}

int bfs(){
    queue<int> q;
    memset(d,-1,sizeof(d));
    for(int i=0;i<=1000;i++)
     if(c[i]){
          int go=i-n;
          q.push(1000+go);
          d[1000+go]=1;
     }
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=0;i<=1000;i++)
         if(c[i]){
              int go=i-n;
              if(go+x>=0&&go+x<=2000&&d[go+x]==-1){
                   d[go+x]=d[x]+1;
                   q.push(go+x);
                   if(go+x==1000) return d[go+x];
             }
         }
    }
    return -1;
}

int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    read(n);read(k);
    for(int i=1;i<=k;i++){
        int x;read(x);
        c[x]=true;
    }
    printf("%d",bfs());
}
d

 

posted @ 2019-10-09 21:10  _JSQ  阅读(113)  评论(0编辑  收藏  举报