csp-s测试41 T2 影子

1、并查集

可以并查集:
考虑对点权的限制。
尝试逐点枚举点权,向点权大于等于自己的节点扩展,计算最大路径。
优化:瓶颈在于还是有很多重复的。
上述的每个节点扩展后形成的连通块点集成为一个集合,
从大点权到小点权只要集合拓展。
维护集合:考虑并查集
点权排序,维护集合内最长链即可。

nlog

#include<bits/stdc++.h>
#define F(i,a,b) for(rg int i=a;i<=b;++i)
#define rg register
#define LL long long
#define il inline
#define pf(a) printf("%d ",a)
#define phn puts("")
using namespace std;
#define int LL
int read();
/*
可以并查集:
考虑对点权的限制。
尝试逐点枚举点权,向点权大于等于自己的节点扩展,计算最大路径。
优化:瓶颈在于还是有很多重复的。
上述的每个节点扩展后形成的连通块点集成为一个集合,
从大点权到小点权只要集合拓展。
维护集合:考虑并查集
点权排序,维护集合内最长链即可。
*/
#define N 100010
int n;
int to[N<<1],fir[N<<1],len[N<<1],head[N],cnt;
int val[N];
int f[N][20],s[N],d[N];
il int max(int x,int y){return x>y?x:y;}
il void add(int x,int y,int w){to[++cnt]=y;fir[cnt]=head[x];head[x]=cnt;len[cnt]=w;}
void dfs(int x,int fa){
    f[x][0]=fa;d[x]=d[fa]+1;
    F(i,1,18)f[x][i]=f[f[x][i-1]][i-1];
    for(int i=head[x];i;i=fir[i]){
        int v=to[i];
        if(v!=fa)s[v]=s[x]+len[i],dfs(v,x);
    }
}
il int lca(int x,int y){
    if(d[x]<d[y])swap(x,y);
    for(int i=18;~i;--i)if(d[f[x][i]]>=d[y])x=f[x][i];
    if(x==y)return x;
    for(int i=18;~i;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
struct node{
    int x,w;
    friend bool operator < (node a,node b){return a.w>b.w;}
}a[N];
int jh[N],w[N],dl[N],dr[N];
bool vis[N];
LL ans;
int find(int x){return jh[x]==x?x:jh[x]=find(jh[x]);}
il int hb(int x,int y){
    x=find(x),y=find(y);
    if(x==y)return x;
    int a[2]={dl[x],dr[x]},b[2]={dl[y],dr[y]};
    int mxd=w[y],l=dl[y],r=dr[y];
    F(i,0,1){
        F(j,0,1){
            int dis=s[a[i]]+s[b[j]]-2ll*s[lca(a[i],b[j])];
            if(dis>mxd){
                mxd=dis;l=a[i];r=b[j];
            }
        }
    }
    if(mxd>w[x]){
        w[x]=mxd;dl[x]=l;dr[x]=r;
    }
    jh[y]=x;
    return x;
}
signed main(){
//    freopen("b.in","r",stdin);
    int T=read();
    while(T--){
        cnt=0;memset(head,0,sizeof(head));ans=0;
        n=read();
        F(i,1,n)a[i]=(node){i,read()};
        for(rg int i=2,u,v,val;i<=n;++i)
            u=read(),v=read(),val=read(),add(u,v,val),add(v,u,val);
        dfs(1,0);
        sort(a+1,a+n+1);
        ans=0;
        F(i,1,n){
             jh[i]=i;w[i]=vis[i]=0;dl[i]=dr[i]=i;
        }
        rg int u;
        F(i,1,n){
            u=a[i].x;vis[u]=1;
            for(rg int j=head[u];j;j=fir[j]){
                int v=to[j];
                if(vis[v]){
                    int rt=hb(u,v);
                    ans=max(ans,w[rt]*a[i].w);
                }
            }
        }
        printf("%lld\n",ans);//
    }
}
il int read(){
    rg int s=0;rg char ch;
    while(ch=getchar(),!isdigit(ch));
    for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
    return s;
}
/*
g++ 1.cpp -g
time ./a.out
1
3
1 2 3
1 2 1
1 3 2
*/
View Code

 2、点分治。

也是对上述暴力的优化。

nlog*log,点分×set (第二个log是log当前点儿子。常数小。)

维护当前分治子树内各点的到根距离、min点权。

然后按最小点权大到小sort记录的信息,更新max边,用当前点×max边。

为了避免来自同一子树,要记录该点所属的根的儿子是谁。

multiset维护各儿子当前max边。

wmz用了线段树做set的功能。但是没必要,增加了常数和码量。不如multiset。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lch p<<1
#define rch p<<1|1
using namespace std;
const int N=1e5+7;
const int inf=0x7f7f7f7f;
struct node{
    long long mx;
    bool clear;
}s[N<<2];
inline void down(int p){
    if(!s[p].clear) return ;
    s[lch].clear=s[rch].clear=1;
    s[lch].mx=s[rch].mx=0;
    s[p].clear=0;
}
inline void up(int p){
    s[p].mx=max(s[lch].mx,s[rch].mx);
}
void insert(int p,int l,int r,int pos,long long val){
    if(l==r) return (void)(s[p].mx=max(s[p].mx,val));
    down(p);
    int mid=l+r>>1;
    if(pos<=mid) insert(lch,l,mid,pos,val);
    else insert(rch,mid+1,r,pos,val);
    up(p);
}
long long query(int p,int l,int r,int L,int R){
    if(l>=L&&r<=R) return s[p].mx;
    down(p);
    int mid=l+r>>1;
    long long ans=0;
    if(L<=mid) ans=max(query(lch,l,mid,L,R),ans);
    if(R>mid) ans=max(query(rch,mid+1,r,L,R),ans);
    up(p);
    return ans;
}
struct data{
    int mn,k;
    long long sum;
    data(){}
    data(int mn,long long sum,int k):mn(mn),sum(sum),k(k){}
    inline friend bool operator < (const data &a,const data &b){
        return a.mn>b.mn;
    }
}que[N];
int n,tot,root,sumsz,mn,cnt;
bool v[N];
int head[N],nxt[N<<1],to[N<<1],w[N<<1],d[N],sz[N];
long long ans;
void calc(int x,int from,int mn,long long sum,int k){
    mn=min(mn,d[x]);
    ans=max(ans,sum*mn);
    que[++cnt]=data(mn,sum,k);
    for(int i=head[x];i;i=nxt[i])
        if(!v[to[i]]&&to[i]!=from) calc(to[i],x,mn,sum+w[i],k);
}
void findroot(int x,int from,int mxp=0){
    sz[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        if(to[i]==from||v[to[i]]) continue;
        findroot(to[i],x);
        sz[x]+=sz[to[i]];
        if(sz[to[i]]>mxp) mxp=sz[to[i]];
    }
    if(sumsz-sz[x]>mxp) mxp=sumsz-sz[x];
    if(mxp<mn) mn=mxp,root=x;
}
void solve(int x,int num=0){
    v[x]=1; cnt=0;
    for(int i=head[x];i;i=nxt[i])
        if(!v[to[i]]) calc(to[i],x,d[x],w[i],++num);
    sort(que+1,que+cnt+1);
    for(int i=1;i<=cnt;++i){
        if(i!=1){
            long long sum=0;
            if(que[i].k!=1) sum=max(sum,query(1,1,num,1,que[i].k-1));
            if(que[i].k!=num) sum=max(sum,query(1,1,num,que[i].k+1,num));
            ans=max(ans,que[i].mn*(sum+que[i].sum));
        }
        insert(1,1,num,que[i].k,que[i].sum);
    }
    s[1].clear=1;
    for(int i=head[x];i;i=nxt[i]){
        if(v[to[i]]) continue;
        sumsz=sz[to[i]]; mn=N;
        findroot(to[i],x);
        solve(root);
    }
}
inline void add(int a,int b,int val){
    nxt[++tot]=head[a];
    head[a]=tot;
    to[tot]=b;
    w[tot]=val;
}
inline int read(register int x=0,register char ch=getchar(),bool f=0){
    while(!isdigit(ch)) f=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
int main(){
    //freopen("b.in","r",stdin);
    int T=read();
    while(T--){
        n=read(); ans=0; tot=0;
        memset(head,0,sizeof(head));
        memset(v,0,sizeof(v));
        for(int i=1;i<=n;++i) d[i]=read();
        for(int i=1,a,b,val;i<n;++i){
            a=read(); b=read(); val=read();
            add(a,b,val); add(b,a,val);
        }
        findroot(1,1);
        solve(1);
        printf("%lld\n",ans);
    }
    return 0;
}
/*
g++ bf.cpp -g
./a.out

*/
View Code

 

posted @ 2019-09-10 10:50  seamtn  阅读(143)  评论(0编辑  收藏  举报