bzoj3545: [ONTAK2010]Peaks(主席树+最小生成树)

17 篇文章 0 订阅
11 篇文章 0 订阅

题目传送门

解法:
好题啊。
只会不强制在线的。
强制在线的表示不会。

离线。
首先x可以到达的点其实都可以互相到达。
那么我们可以看作是一个联通块。
要求边权尽量小其实就是最小生成树啊。

离线首先按每次询问的x排序。
然后依次建小于等于x的最小生成树。
那么当前v所在的联通块的第k大实际上就是答案。
并查集压缩路径的时候合并一下主席树即可。

吐槽:
时限太少了。40s好不好。
本机太慢每个点跑了5秒。
提交TLE。
这时候当然是找卡常大神来优化啦。
太神了%%%。
玄学快速读.
还有一堆我以前代码里找不到的东西。。
把我五秒的程序硬是弄到三秒。
然后7000ms+过了

代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
inline char nc() {
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(void) {
    char ch=nc();
    int sum=0;
    while(!(ch>='0'&&ch<='9'))
        ch=nc();
    while(ch>='0'&&ch<='9')
        sum=(sum<<3)+(sum<<1)+(ch^48),ch=nc();
    return sum;
}
struct node {int lc,rc,c;}t[2000001];int cnt,rt[200001];
inline void build(int &u,int l,int r,int p) {
    if(u==0)u=++cnt;t[u].c++;
    if(l==r)return ;int mid=(l+r)/2;
    if(p<=mid)build(t[u].lc,l,mid,p);
    else build(t[u].rc,mid+1,r,p);
}
int fa[100001];inline int findfa(int x) {if(fa[x]!=x)fa[x]=findfa(fa[x]);return fa[x];}
void Merge(int &u1,int u2) {
    if(u1==0) {u1=u2;return ;}if(u2==0)return ;
    t[u1].c+=t[u2].c;
    Merge(t[u1].lc,t[u2].lc);
    Merge(t[u1].rc,t[u2].rc);
}
int S[100001];
inline int find(int u,int l,int r,int k) {
    if(t[u].c<k)return -1;
    if(l==r)return S[l];
    int mid=(l+r)/2;
    if(k<=t[t[u].rc].c)return find(t[u].rc,mid+1,r,k);
    else return find(t[u].lc,l,mid,k-t[t[u].rc].c);
}
struct trnode {int x,y,c,id,ans;}a[500001],Q[500001];int len;
bool cmp(trnode n1,trnode n2) {return n1.c<n2.c;}
bool cmp2(trnode n1,trnode n2) {return n1.y<n2.y;}
struct edge {int x,id;}s[500001];
bool cmp1(edge n1,edge n2) {return n1.x<n2.x;}
bool cmp3(trnode n1,trnode n2) {return n1.id<n2.id;}
int main() {
    //freopen("3545.in","r",stdin);freopen("3545.out","w",stdout);
    int n,m;n=read(),len=read(),m=read();cnt=0;
    for(int i=1;i<=n;i++) {s[i].x=read();s[i].id=i;}
    sort(s+1,s+1+n,cmp1);int tot=0;
    for(int i=1;i<=n;i++) {
        if(s[i].x!=s[i-1].x)tot++;S[tot]=s[i].x;
        build(rt[s[i].id],1,n,tot);
    }
    for(register int i=1;i<=len;++i) {a[i].x=read();a[i].y=read();a[i].c=read();}
    for(register int i=1;i<=m;++i) {
        Q[i].x=read();Q[i].y=read();Q[i].c=read();Q[i].id=i;
    }
    for(int i=1;i<=n;++i)fa[i]=i;
    sort(a+1,a+len+1,cmp);sort(Q+1,Q+1+m,cmp2);int tt=1;
    for(register int i=1;i<=m;++i) {
        for(register int j=tt;j<=len;++j) {
            if(a[j].c>Q[i].y) {tt=j;break;}
            int xx=findfa(a[j].x),yy=findfa(a[j].y);
            if(xx!=yy) {fa[xx]=yy;Merge(rt[yy],rt[xx]);}
            if(j==len)tt=len+1;
        }Q[i].ans=find(rt[findfa(Q[i].x)],1,n,Q[i].c);
    }sort(Q+1,Q+1+m,cmp3);
    for(int i=1;i<=m;i++)printf("%d\n",Q[i].ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值