LOJ137(LOJ6021)

LOJ137

这题,真心整死人。。
首先,国家集训队论文里有一篇有关文章:

郭华阳 《RMQ与LCA问题》

然后有一个定理:

两点间最小瓶颈路一定是最小生成树上两点间唯一路径上的最大边

所以先求原图的最小生成树,这题就变成求树上两点间唯一路径上的最大边了
然后倍增? O(10000000log) 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表)吧
举个栗子:
LOJ137(LOJ6021)_第1张图片
这样我们要搞出这样的DFS序:
LOJ137(LOJ6021)_第2张图片
(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(NlogN) O ( N ∗ l o g N ) RMQ+ O(1) O ( 1 ) 回答询问了
总复杂度 O(NlogN+ElogM+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;
}

你可能感兴趣的:(LOJ,预处理,LCA,最小生成树)