LOJ137
这题,真心整死人。。
首先,国家集训队论文里有一篇有关文章:
然后有一个定理:
两点间最小瓶颈路一定是最小生成树上两点间唯一路径上的最大边
所以先求原图的最小生成树,这题就变成求树上两点间唯一路径上的最大边了
然后倍增? O(10000000∗log) O ( 10000000 ∗ l o g ) ?T到飞起。。再怎么卡常,LOJ跑得再快,依然为 O(不能过) O ( 不 能 过 )
#pragma GCC optimize(6)
#include
#include
#define rnd() (A=(A*B+C)%P)
#define __R register
#define IN inline
using namespace std;
const int maxn=(7e4)+5,maxE=(1e5)+5,Log=20,TT=1000000007;
templateIN Tp Max(Tp x,Tp y){return x>y?x:y;}
int Ans,n,m,Q,A,B,C,P,fa[maxn],cnt[maxn];
struct ff{
int x,y,w;
IN bool operator <(const ff b)const{return wint getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int tot,lnk[maxn],nxt[maxn<<1],son[maxn<<1],w[maxn<<1],dep[maxn],up[maxn][Log],val[maxn][Log],pow[Log],D[maxn];
IN void add_e(int x,int y,int z){son[++tot]=y,w[tot]=z,nxt[tot]=lnk[x],lnk[x]=tot;}
IN void DFS(int x,int dad){
dep[x]=dep[dad]+1;
for(__R int i=1;pow[i]<=dep[x];i++) up[x][i]=up[up[x][i-1]][i-1],val[x][i]=Max(val[x][i-1],val[up[x][i-1]][i-1]);
for(__R int j=lnk[x];j;j=nxt[j])if(son[j]!=dad){
up[son[j]][0]=x,val[son[j]][0]=w[j];
DFS(son[j],x);
}
}
IN int LCA(int x,int y){
int ans=0;
if(dep[x]y]){int t=x;x=y,y=t;}
while(dep[x]>dep[y]){if(ansx][D[dep[x]-dep[y]]-1]) ans=val[x][D[dep[x]-dep[y]]-1];x=up[x][D[dep[x]-dep[y]]-1];}
if(x==y) return ans;
for(__R int k=D[dep[x]-1]-1;k>=0;k--) if(up[x][k]!=up[y][k]){if(ansx][k]) ans=val[x][k];if(ansy][k]) ans=val[y][k];x=up[x][k],y=up[y][k];}
if(ansx][0]) ans=val[x][0];if(ansy][0]) ans=val[y][0];
return ans;
}
IN char gt(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
IN int read(){
int ret=0;char ch=gt();
while(ch<'0'||ch>'9') ch=gt();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret;
}
int main(){
n=read(),m=read();
for(__R int i=1;i<=m;i++) E[i]=(ff){read(),read(),read()};sort(E+1,E+1+m);
for(__R int i=1;i<=n;i++) fa[i]=i,cnt[i]=1;
for(__R int i=1,fx,fy;i<=m;i++)if((fx=getfa(E[i].x))!=(fy=getfa(E[i].y))){
if(cnt[fx]else fa[fy]=fx,cnt[fx]+=cnt[fy];
add_e(E[i].x,E[i].y,E[i].w),add_e(E[i].y,E[i].x,E[i].w);
}
for(__R int i=0;(pow[i]=(1<for(__R int i=1,k=0;i<=n;i++) D[i]=D[i-1]+((1<1])==i);
DFS(1,0);
Q=read();
A=read(),B=read(),C=read(),P=read();
while(Q--){
int u=rnd()%n+1,v=rnd()%n+1;
if(u==v) continue;
if((Ans+=LCA(u,v))>=TT) Ans-=TT;
}
printf("%d\n",Ans);
return 0;
}
再怎么办呢?又有一个定理:
最小生成树上两点间唯一路径的最大边,即为做Kruscal算法时第一次将两点联通的边
所以我们可以这样建图:
int Tot=n;
for(__R int i=1,fx,fy,tp=1;tp!=n&&i<=m;i++)if((fx=getfa(E[i].x))!=(fy=getfa(E[i].y))){
val[++Tot]=E[i].w,++tp;
fa[fx]=Tot,fa[fy]=Tot;
add_e(Tot,fx),add_e(Tot,fy);
}
这样每次询问就相当与询问 val[LCA(x,y)] v a l [ L C A ( x , y ) ] 了,成功将问题转化成了LCA
然后开心地写个Tarjan。。 O(凉凉) O ( 凉 凉 )
#pragma GCC optimize(2)
#include
#include
#include
#define rnd() (A=(A*B+C)%P)
#define __R register
#define IN inline
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int maxn=(7e4)+5,maxQ=(1e7)+5,maxE=(1e5)+5,Log=20,TT=1000000007;
static char buf[100000],*p1=buf,*p2=buf;
int Ans,n,m,Q,A,B,C,P,fa[maxn<<1],val[maxn<<1],cnt[maxn<<1];bool vis[maxn<<1];
IN int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
struct ff{
int x,y,w;
IN bool operator <(const ff b)const{return wint tot,son[maxQ<<1],nxt[maxQ<<1],lnk[maxn<<1];
void add_e(int x,int y){son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}
}GA,GB;
IN void DFS(int x){
vis[x]=1;int fx=getfa(x);
for(__R int j=GA.lnk[x];j;j=GA.nxt[j]) if(!vis[GA.son[j]]){
DFS(GA.son[j]);
int fy=getfa(GA.son[j]);
if(fx!=fy) fa[fy]=fx;
}
for(__R int j=GB.lnk[x];j;j=GB.nxt[j]) if(vis[GB.son[j]]) if((Ans+=val[getfa(GB.son[j])])>=TT) Ans-=TT;
}
IN int read(){
int ret=0;char ch=gt();
while(ch<'0'||ch>'9') ch=gt();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret;
}
int main(){
n=read(),m=read();
for(__R int i=1;i<=m;i++) E[i]=(ff){read(),read(),read()};sort(E+1,E+1+m);
int tot=n;
for(__R int i=1,ti=(n<<1);i<=ti;i++) fa[i]=i;
for(__R int i=1,fx,fy;i<=m;i++)if((fx=getfa(E[i].x))!=(fy=getfa(E[i].y))){
val[++tot]=E[i].w;
fa[fx]=tot,fa[fy]=tot;
GA.add_e(tot,fx),GA.add_e(tot,fy);
}
Q=read();
A=read(),B=read(),C=read(),P=read();
while(Q--){
int u=rnd()%n+1,v=rnd()%n+1;
if(u!=v) GB.add_e(u,v),GB.add_e(v,u);
}
for(int i=1;i<=tot;i++) fa[i]=i,cnt[i]=1;
DFS(tot);
printf("%d\n",Ans);
return 0;
}
再分析,询问巨多,N不太大。。DFS序+RMQ(ST表)吧
举个栗子:
这样我们要搞出这样的DFS序:
(T_in[x]表示第一次访问到x的时间)
void DFS(int x){
id[T_in[x]=++T]=x;
for(__R int j=lnk[x];j;j=nxt[j]) dep[son[j]]=dep[x]+1,DFS(son[j]),id[++T]=x;
}
如果我们要求 LCA(d,f) L C A ( d , f ) ,我们发现 d d 第一次出现在4, f f 第一次出现在9,由 DFS D F S 的性质可以知道,在4~9之间深度最小的点就是LCA
所以 LCA(x,y)=min_dep(id[T_in[x]——T_in[y]]) L C A ( x , y ) = m i n _ d e p ( i d [ T _ i n [ x ] — — T _ i n [ y ] ] )
然后就可以 O(N∗logN) O ( N ∗ l o g N ) RMQ+ O(1) O ( 1 ) 回答询问了
总复杂度 O(N∗logN+E∗logM+Q) O ( N ∗ l o g N + E ∗ l o g M + Q )
PS:这种题,忍不住加了点常数优化
#include
#define rnd() (A=(A*B+C)%P)
#define __R register
#define IN inline
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int maxn=(7e4)+5,maxQ=(1e7)+5,maxE=(1e5)+5,Log=22,TT=1000000007;
static char buf[100000],*p1=buf,*p2=buf;
int Ans,n,N,m,Q,A,B,C,P;
struct ff{
int x,y,w;
IN bool operator <(const ff b)const{return wint read(){
int ret=0;char ch=gt();
while(ch<'0'||ch>'9') ch=gt();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret;
}
int fa[maxn<<1],val[maxn<<1];
IN int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int tot,son[maxn<<1],nxt[maxn<<1],lnk[maxn<<1],id[maxn<<2],T,T_in[maxn<<1],dep[maxn<<1];
IN void add_e(int x,int y){son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}
void DFS(int x){
id[T_in[x]=++T]=x;
for(__R int j=lnk[x];j;j=nxt[j]) dep[son[j]]=dep[x]+1,DFS(son[j]),id[++T]=x;
}
int f[maxn<<2][Log],Pow[Log+5],LoG[maxn<<2];
IN void make_ST(){
for(__R int i=0;(Pow[i]=(1<for(__R int i=1;i<=T;i++) LoG[i]=LoG[i-1]+(Pow[LoG[i-1]]==i),f[i][0]=id[i];
for(__R int j=1;Pow[j]<=T;j++)
for(__R int i=1,ti=T+1-Pow[j];i<=ti;i++) f[i][j]=(dep[f[i][j-1]]1]][j-1]]?f[i][j-1]:f[i+Pow[j-1]][j-1]);
}
int main(){
N=(n=read())<<1,m=read();
for(__R int i=1;i<=m;i++) E[i]=(ff){read(),read(),read()};sort(E+1,E+1+m);
int Tot=n;
for(__R int i=1;i<=N;i++) fa[i]=i;
for(__R int i=1,fx,fy,tp=1;tp!=n&&i<=m;i++)if((fx=getfa(E[i].x))!=(fy=getfa(E[i].y))){
val[++Tot]=E[i].w,++tp;
fa[fx]=Tot,fa[fy]=Tot;
add_e(Tot,fx),add_e(Tot,fy);
}
dep[tot]=1,DFS(Tot);make_ST();
Q=read();
A=read(),B=read(),C=read(),P=read();
while(Q--){
int u=T_in[rnd()%n+1],v=T_in[rnd()%n+1],t;
if(u==v) continue;
if(u>v){int tep=u;u=v,v=tep;}
int ID=f[u][t=LoG[v-u+1]-1];
if(dep[ID]>dep[f[v-Pow[t]+1][t]]) ID=f[v-Pow[t]+1][t];
if((Ans+=val[ID])>=TT) Ans-=TT;
}
printf("%d\n",Ans);
return 0;
}