zoukankan      html  css  js  c++  java
  • 题解 Codeforces 404E The Classic Problem

    题意

    给定一张 \(n\) 个点 \(m\) 条边的有向图,第 \(i\) 条边的边权为 \(2^{x_i}\),求 \(s\)\(t\) 的最短路。

    答案对 \(10^9+7\) 取模。

    \(1 \leq n, m \leq 10^5,0 \leq x_i \leq 10^5\)

    题解

    求最短路?上 Dijkstra。

    接下来的问题是需要一个数据结构维护这大的离谱的最短路,支持加上一个 \(2\) 的非负整数次幂和比较大小。

    注意到,边权都是 \(2\) 的非负整数次幂,那么我们可以把加法和比较大小在二进制意义下处理。此时加法变为区间赋值 \(0\) 和单点赋值 \(1\) 的操作,可以用主席树维护。

    对于比较大小,我们可以记录下每个区间的 hash 值。如果左区间的 hash 值相等则比较右区间,否则比较左区间,直到递归到叶节点为止。

    实现略微复杂。

    # include <bits/stdc++.h>
    
    typedef long long ll;
    const int N=200010,INF=0x3f3f3f3f;
    const ll MOD=1e9+7;
    struct Edge{
    	int to,next,v;
    }edge[N<<1];
    int head[N],sum;
    ll powd[N];
    
    
    struct Node{
    	int lc,rc;
    	bool full; // 记录区间是否全 1
    	ll w;
    }tree[N*40];
    
    int root[N],cnt;
    int n,m,xlim;
    int s,t;
    int pre[N],preval[N];
    bool c[N];
    bool calc[N];
    int sta[N],scnt;
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')
    		res=res*10+c-48;
    	return res*f;
    }
    inline int& lc(int x){
    	return tree[x].lc;
    }
    inline int& rc(int x){
    	return tree[x].rc;
    }
    inline void pushup(int k){
    	tree[k].w=(tree[lc(k)].w+tree[rc(k)].w)%MOD;
    	tree[k].full=tree[lc(k)].full&&tree[rc(k)].full;
    	return;
    }
    int liquery(int &k,int l,int r){ // 查询区间内部最长的全 1 前缀
    	if(!k)
    		return 0;
    	int mid=(l+r)>>1;
    	return (tree[lc(k)].full)?(mid-l+1+liquery(rc(k),mid+1,r)):(liquery(lc(k),l,mid));
    }
    std::pair <int,bool> query(int &k,int l,int r,int x){
        // 查询 l r 区间内 从 x 开始最长的连续 1 长度
        // pair.first 是 1 的长度 pair.second 表示区间右端点是否为 1
    	if(!k)
    		return std::make_pair(0,false);
    	if(l==r)
    		return std::make_pair((tree[k].w>0),(tree[k].w>0));
    	int mid=(l+r)>>1;
    	if(x>mid)
    		return query(rc(k),mid+1,r,x); // 起点在右侧 那就没啥事了
    	std::pair <int,bool> res=query(lc(k),l,mid,x); // 查询左侧的答案
    	if(!res.second) // 如果左区间的右端点不为 1 那么说明加上右区间也不可能凑出连续的 1 了
    		return res;
    	if(tree[rc(k)].full) // 如果右区间全是 1 就不用查询 直接返回区间长度
    		return std::make_pair(res.first+r-mid,true);
    	return std::make_pair(liquery(rc(k),mid+1,r)+res.first,false); // 查询长度(该操作最多调用 1 次)
    }
    inline void changezero(int &k,int lst,int l,int r,int L,int R){ // 区间赋值 0
    	if(!lst)
    		return;
    	if(L>R){
    		k=lst;
    		return;
    	}
    	if(L<=l&&r<=R){
    		k=0;
    		return;
    	}
    	k=++cnt,tree[k]=tree[lst];
    	int mid=(l+r)>>1;
    	if(L<=mid)
    		changezero(lc(k),lc(lst),l,mid,L,R);
    	if(mid<R)
    		changezero(rc(k),rc(lst),mid+1,r,L,R);
    	pushup(k);
    	return;
    }
    void changeone(int &k,int lst,int l,int r,int x){ // 单点赋值 1
    	k=++cnt,tree[k]=tree[lst];
    	if(l==r){
    		tree[k].w=powd[l],tree[k].full=true;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid)
    		changeone(lc(k),lc(lst),l,mid,x);
    	else
    		changeone(rc(k),rc(lst),mid+1,r,x);
    	pushup(k);
    	return;
    }
    bool compare(int a,int b,int l,int r){ // is (a>b) true ?
    	if(l==r){
    		return tree[a].w>tree[b].w;
    	}
    	int mid=(l+r)>>1;
    	if(tree[rc(a)].w==tree[rc(b)].w)
    		return compare(lc(a),lc(b),l,mid);
    	return compare(rc(a),rc(b),mid+1,r);
    }
    struct Heapval{
    	int id,rt;
    	bool operator < (const Heapval &rhs) const{
    		return compare(rt,rhs.rt,0,xlim);
    	}
    };
    std::priority_queue <Heapval> q;
    inline void add(int x,int y,int v){
    	edge[++sum].to=y;
    	edge[sum].next=head[x];
    	edge[sum].v=v;
    	head[x]=sum;
    	return;
    }
    inline void Dijkstra(void){
    	q.push((Heapval){s,0});
    	calc[s]=true;
        // calc 表示当前的值是否被计算过
        // 如果没有被计算过默认为无穷大 不进行主席树的大小比较
    	while(!q.empty()){
    		int i=q.top().id,irt=q.top().rt;
    		q.pop();
    		if(c[i])
    			continue;
    		c[i]=true;
    		for(int j=head[i];j;j=edge[j].next){
    			int to=edge[j].to;
    			if(c[to])
    				continue;
    			int sid=edge[j].v+query(irt,0,xlim,edge[j].v).first,temp=0;
    			changezero(temp,irt,0,xlim,edge[j].v,sid-1);
    			changeone(temp,temp,0,xlim,sid); // 模拟加法
    			if(!calc[to]||(calc[to]&&compare(root[to],temp,0,xlim))){
    				calc[to]=true;
    				pre[to]=i,preval[to]=edge[j].v,root[to]=temp;
    				q.push((Heapval){to,root[to]});
    			}
    		}
    	}
    	return;
    }
    int main(void){
    	n=read(),m=read();
    	for(int i=1;i<=m;++i){
    		int u=read(),v=read(),w=read();
    		add(u,v,w),add(v,u,w),xlim=std::max(xlim,w);
    	}
    	s=read(),t=read();
    	powd[0]=1;
    	for(int i=1;i<=2e5-10;++i){
    		powd[i]=powd[i-1]*2ll%MOD;
    	}
    	xlim+=30; // 最多可能往后进 log 次位
    	Dijkstra();
    	if(!c[t])
    		puts("-1");
    	else{
    		ll ans=0;
    		while(pre[t]){
    			ans=(ans+powd[preval[t]])%MOD,sta[++scnt]=t,t=pre[t];
    		}
    		printf("%lld\n%d\n%d ",ans,scnt+1,s);
    		for(int i=scnt;i;--i)
    			printf("%d ",sta[i]);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    jwplayer
    jwPlayer为js预留的回调方法
    Java的内存--内存溢出vs内存泄露(2)
    java:LeakFilling(Hibernate)
    java:Hibernate框架1(环境搭建,Hibernate.cfg.xml中属性含义,Hibernate常用API对象,HibernteUitl,对象生命周期图,数据对象的三种状态,增删查改)
    java:struts框架(网路静态U盘项目)
    java:struts框架5(Converter,Validation,Tags(Object-Graph Navigation Language))
    java:struts框架4(Ajax)
    java:struts框架3(自定义拦截器,token令牌,文件上传和下载(单/多))
    java:struts框架2(方法的动态和静态调用,获取Servlet API三种方式(推荐IOC(控制反转)),拦截器,静态代理和动态代理(Spring AOP))
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/15256799.html
Copyright © 2011-2022 走看看