Codeforces 464E #265 (Div. 1) E. The Classic Problem 主席树+Hash

E. The Classic Problem

http://codeforces.com/problemset/problem/464/E

题意:给你一张无向带权图,求S-T的最短路,并输出路径。边权为2^xi。xi≤105,n≤105,m≤105.

想法:边权太大了,可以用数组按二进制存下来。带高精度跑太费事了。

观察一下,这里距离的更新:c=(a,b),用dis[a]更新dis[b]

①dis[b][c]=0,直接赋为1.只有一个数字改变。

②dis[b][c]=1,需要进位。考虑极端情况数据,xi都是一个数,那么需要进位O(m/2+m/4+m/8+...+1)≈O(m)

所以总改变次数为O(m),使用主席树维护这个二进制数组。

然后比较大小,可以在主席树上二分,如果左儿子一样就往右儿子走,反之走左儿子。判断是否一样可以用Hash。一次比较为O(logm).

于是dijkstra+heap+主席树:

空间复杂度O(mlogm).

时间复杂度O(mlog^2m).

ps:因为有进位要给主席树预留logm个空间。

  1 #include<cstdio>
  2 #include<algorithm>
  3 const int len(110000),lem(8000000),HP(47),MP(666696667),RP(1000000007);
  4 struct ChairManTree{int nx[2],sum;}tree[lem+10];
  5 int stot,root[len+10];int H[len+10],b[len+10],tp;
  6 struct Node{int nd,nx,co;}bot[len*2+10];
  7 int tot,first[len+10];
  8 int n,m,v[len+10],u[len+10],x[len+10],axle[len+10],up,big,s,t,last[len+10];
  9 void add(int a,int b,int c){bot[++tot]=(Node){b,first[a],c};first[a]=tot;}
 10 int two(int x)
 11 {
 12     int l=1,r=up,ans=1,mid;
 13     while(l<=r)
 14     if(axle[mid=(l+r)>>1]>=x){ans=mid;l=mid+1;}else r=mid-1;
 15     return ans;
 16 }
 17 void update(int k,int l,int r)
 18 {
 19     tree[k].sum=(1ll*tree[tree[k].nx[0]].sum*H[(r-l+1)>>1]+tree[tree[k].nx[1]].sum)%MP;
 20 }
 21 void build(int k,int l,int r,int x)
 22 {
 23     if(l==r){tree[k].sum=x;return;}
 24     int mid=(l+r)>>1;
 25     tree[k].nx[0]=++stot;build(stot,l,mid,x);
 26     tree[k].nx[1]=++stot;build(stot,mid+1,r,x);
 27     update(k,l,r);
 28 }
 29 int cmp(int k1,int k2)//-:k1<k2,+:k2>k1,0:k1=k2,return +-pos
 30 {
 31     int l=1,r=up+1,mid;
 32     while(l!=r)
 33     {
 34         mid=(l+r)>>1;
 35         if(tree[tree[k1].nx[0]].sum==tree[tree[k2].nx[0]].sum)
 36         k1=tree[k1].nx[1],k2=tree[k2].nx[1],l=mid+1;
 37         else k1=tree[k1].nx[0],k2=tree[k2].nx[0],r=mid;
 38     }
 39     if(tree[k1].sum==tree[k2].sum)return 0;
 40     if(tree[k1].sum<tree[k2].sum)return -l;
 41     return l;
 42 }
 43 int insert(int k,int l,int r,int &x)
 44 {
 45     int z=++stot;
 46     if(l==r)
 47     {
 48         if(tree[k].sum)tree[z].sum=0,x--;//进位
 49         else tree[z].sum=1;return z;
 50     }
 51     int mid=(l+r)>>1,tmp;tree[z]=tree[k];
 52     if(x>mid){tmp=insert(tree[k].nx[1],mid+1,r,x);tree[z].nx[1]=tmp;}
 53     if(x<=mid){tmp=insert(tree[k].nx[0],l,mid,x);tree[z].nx[0]=tmp;}
 54     update(z,l,r);
 55     return z;
 56 }
 57 void dfs(int x,int l,int r)
 58 {
 59     if(l==r){b[l]=tree[x].sum;return;}
 60     int mid=(l+r)>>1;
 61     dfs(tree[x].nx[0],l,mid);
 62     dfs(tree[x].nx[1],mid+1,r);
 63 }
 64 struct data
 65 {
 66     int x,y;
 67 bool operator <(const data &A)const//默认小根堆
 68     {
 69         return (cmp(x,A.x)>0);
 70     }
 71 }q[len*2+10];int qfree;
 72 void Dijkstra()
 73 {
 74     for(int i=1;i<=n;i++)root[i]=root[n+1];
 75     root[s]=root[0];q[qfree=1]=(data){root[s],s};
 76     while(qfree)
 77     {
 78         data now=q[1];std::pop_heap(q+1,q+1+qfree);qfree--;
 79         while(root[now.y]!=now.x&&qfree){now=q[1];std::pop_heap(q+1,q+1+qfree);qfree--;}
 80         if(root[now.y]!=now.x&&!qfree)break;
 81         for(int v=first[now.y];v;v=bot[v].nx)
 82         {
 83             int tmp=insert(root[now.y],1,up+1,bot[v].co);
 84             int pos=cmp(tmp,root[bot[v].nd]);
 85             if (pos>=0)continue;
 86             last[bot[v].nd]=now.y;
 87             root[bot[v].nd]=tmp;
 88             q[++qfree]=(data){root[bot[v].nd],bot[v].nd};
 89             std::push_heap(q+1,q+1+qfree);
 90         }
 91     }
 92 }
 93 void put()
 94 {
 95     if(root[t]==root[n+1])printf("-1");
 96     else
 97     {
 98         if(tree[root[t]].sum==tree[root[0]].sum)printf("0\n");
 99         else
100         {
101             dfs(root[t],1,up+1);
102             int tmp=0,j=1,l=0;
103             for(int i=up;i>=1;i--)
104             {
105                 while(l<axle[i]){l++;j=(j+j)%RP;}
106                 tmp=(tmp+1ll*j*b[i])%RP;
107             }
108             printf("%d\n",tmp);
109         }
110         int now=t;b[tp=1]=t;
111         while(last[now])
112         {
113             now=last[now];
114             b[++tp]=now;
115         }
116         printf("%d\n",tp);
117         for(int i=tp;i>=1;i--)
118         printf("%d ",b[i]);
119     }
120 }
121 bool cmp2(int a,int b){return a>b;}
122 int main()
123 {
124     freopen("C.in","r",stdin);
125     freopen("C.out","w",stdout);
126     scanf("%d%d",&n,&m);
127     for(int i=1;i<=m;i++)
128     {
129         scanf("%d%d%d",v+i,u+i,x+i);
130         if(x[i]>big)big=x[i];
131     }
132     scanf("%d%d",&s,&t);
133     for(int i=1;i<=big+40;i++)axle[i]=big+40-i;up=big+40;
134     H[0]=1;for(int i=1;i<=up+1;i++)H[i]=(1ll*H[i-1]*HP)%MP;
135     for(int i=1;i<=m;i++)
136     {
137         x[i]=two(x[i]);
138         add(v[i],u[i],x[i]);
139         add(u[i],v[i],x[i]);
140     }
141     root[0]=++stot;build(root[0],1,up+1,0);//zero
142     root[n+1]=++stot;build(root[n+1],1,up+1,1);//INF
143     Dijkstra();
144     put();
145     return 0;
146 }
时间: 2024-11-17 00:41:10

Codeforces 464E #265 (Div. 1) E. The Classic Problem 主席树+Hash的相关文章

Codeforces Round #406 (Div. 2) E. Till I Collapse(主席树)

题目链接:Codeforces Round #406 (Div. 2) E. Till I Collapse 题意: 给你n个数,对于每一个k(1<=k<=n),划分区间,每个区间只能有k个不同的数字, 问最小的划分区间的个数. 题解: 用主席树倒着将数插入,对于每个区间询问第k个不同数的位置就行了. #include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=b;i++) #define ___ freopen("

Codeforces Round #265 (Div. 2)

Codeforces Round #265 (Div. 2) 题目链接 A:把数字变换后比较一下几个不一样即可 B:连续2个以上0当作一次操作,开头的0和结尾的0可以忽略 C:贪心从末尾去构造,由于保证一开始是回文,所以保证修改后出现回文只可能为长度2或3的,这样的话判断复杂度就很小了 D:暴力枚举情况,然后判断 E:把操作逆过来处理出每个数字对应的位数和相应数字,然后在for一遍计算答案即可 代码: A: #include <cstdio> #include <cstring>

Codeforces Round #265 (Div. 2) 465A. inc ARG(数学题)

题目链接:http://codeforces.com/problemset/problem/465/A Sergey is testing a next-generation processor. Instead of bytes the processor works with memory cells consisting of n bits. These bits are numbered from 1 to n. An integer is stored in the cell in t

Codeforces Round #265 (Div. 1)

A No to Palindromes! 题意:给一个长度为n的用前m个字符构成的字符串,定义一个字符串是好的当且仅当他的每个子串都不是回文的,现在给出一个好的字符串,求比他字典序大的第一个好的字符串. 题解:从后往前每一位枚举,若把当前枚举的位改成ch后为好的串,只需要判断他和他前面的一个字符是否相同构成长度为2的回文串,或者和他前面的前面的两个字符构成长度为3的回文串. 于是找到第一个可以换的位置,后面每个位置从'a'开始枚举可以取得的第一个字符即可. 1 #include <cstdio>

Codeforces Round #265 (Div. 2) 题解

A:给你一个二进制数,问你加一以后改变多少位 解题思路:乱搞 解题代码: 1 // File Name: a.cpp 2 // Author: darkdream 3 // Created Time: 2014年09月07日 星期日 23时27分31秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #include<set> 9 #include<deque> 10 #includ

Codeforces Round #265 (Div. 2) B. Inbox (100500)

Over time, Alexey's mail box got littered with too many letters. Some of them are read, while others are unread. Alexey's mail program can either show a list of all letters or show the content of a single letter. As soon as the program shows the cont

Codeforces Round #265 (Div. 2) C. No to Palindromes!

Paul hates palindromes. He assumes that string s is tolerable if each its character is one of the first p letters of the English alphabet and s doesn't contain any palindrome contiguous substring of length 2 or more. Paul has found a tolerable string

Codeforces Round #265 (Div. 2) D. Restore Cube

Peter had a cube with non-zero length of a side. He put the cube into three-dimensional space in such a way that its vertices lay at integer points (it is possible that the cube's sides are not parallel to the coordinate axes). Then he took a piece o

Codeforces Round #149 (Div. 2) E. XOR on Segment (线段树成段更新+二进制)

题目链接:http://codeforces.com/problemset/problem/242/E 给你n个数,m个操作,操作1是查询l到r之间的和,操作2是将l到r之间的每个数xor与x. 这题是线段树成段更新,但是不能直接更新,不然只能一个数一个数更新.这样只能把每个数存到一个数组中,长度大概是20吧,然后模拟二进制的位操作.仔细一点就行了. 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath>