首页 > 其他分享 >2024 ICPC Asia Pacific Championship-K-线段树合并or主席树

2024 ICPC Asia Pacific Championship-K-线段树合并or主席树

时间:2024-03-04 16:26:24浏览次数:31  
标签:Championship int auto mid Pacific ICPC ret LCA itr

比赛链接:https://codeforces.com/contest/1938
给一棵有根树,执行以下代码:

let L be an empty array
for x = 1 to n
	for y = 1 to n
		append ((x - 1) * n * n + (LCA(x, y) - 1) * n + (y - 1)) to L
sort L in non-decreasing order

然后进行 \(q\) 次询问,每次问 \(L\) 中第 \(k\) 个元素是多少。\(1\leq n,q\leq 10^5\).


\(L\) 中的元素可以看成 \(n\) 进制数,每个 \(x\) 出现次数一样,所以先确定 \(x=\lfloor (k-1)/n\rfloor +1\),然后\(k\to (k-1)\bmod n +1\),把询问写成序对 \((x,y,LCA,idx)\) 先挂到每个结点 \(x\) 上,根据下图所示查询 \(x\) 的每个祖先(包括 \(x\) )作为LCA,能有几个点,在这条链上二分能 \(O(n\log^2 n)\) 确定LCA,同时 \(k\to k-\sum_{i=1}^{l-1} f_i\)(其中 \(l\) 是确定的LCA在链上的位置,\(f_i\) 是每个点作为LCA能得到的 \(y\) 的个数,这些都容易处理);考虑到有 \(q\) 个询问,将询问离线下来,一边dfs一边处理。

最后确定 \(y\) ,再次dfs将询问挂在LCA上,在上一步中顺便需要对每个询问记录LCA往 \(x\) 放在往下走的第一个点 \(dir\) 是多少,求 \(y\) 的时候相当于查询 \(LCA\) 的子树扣掉 \(dir\) 的子树的这些结点中,第 \(k\) 大的结点是多少:

在每个节点上开一个动态线段树,结点 \(i\) 初始只有 \(v[i]=1\) ,dfs的时候从叶子往根节点一边回答询问一边合并线段树。因为处理LCA的时候不仅需要LCA的线段树,还需要某个孩子dir的线段树,所以需要对线段树做一个备份:一棵树在处理询问前合并,另一棵在处理询问后合并:

void dfs2(int x){
    for(auto to:G[x])dfs2(to);
    for(auto to:G[x])//merge segment tree 1
    //处理x(其实是LCA)上的每个询问
    for(auto to:G[x])//merge segment tree 2
}

代码:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int LOG=65;
struct Query{
    int x,y,LCA,k,idx,dir;
};
struct Fenwick{
    int a[N],n;
    void init(int _n){n=_n;}
    int lowbit(int x){return x&-x;}
    int query(int x){
        int ret=0;
        for(;x;x-=lowbit(x))ret+=a[x];
        return ret;
    }
    void add(int x,int v){
        for(;x<=n;x+=lowbit(x))a[x]+=v;
    }
    void modify(int x,int v){
        int cur=query(x);
        if(x)cur-=query(x-1);
        add(x,v-cur);
    }
}tr;
struct SegmentTree{
    int rt[N],tr[N*LOG],lson[N*LOG],rson[N*LOG];
    int bin[N*LOG],cb,cnt;
    #define ls (lson[node])
    #define rs (rson[node])
    void del_node(int node){bin[++cb]=node;}
    int new_node(){
        if(cb)return bin[cb--];
        return ++cnt;
    }
    void push_up(int node){
        if(!ls)ls=new_node(),tr[ls]=0;
        if(!rs)rs=new_node(),tr[rs]=0;
        tr[node]=tr[ls]+tr[rs];
    }
    void modify(int &node,int l,int r,int x,int v){
        if(!node)node=new_node();
        if(l==r){
            tr[node]=v;
            return ;
        }
        int mid=(l+r)>>1;
        if(mid>=x)modify(ls,l,mid,x,v);
        else modify(rs,mid+1,r,x,v);
        push_up(node);
    }
    int merge(int &u,int &v,int l,int r){
        if(!u||!v)return u|v;
        if(l==r){
            tr[u]+=tr[v];
            del_node(v);
            return u;
        }
        int mid=(l+r)>>1;
        lson[u]=merge(lson[u],lson[v],l,mid);
        rson[u]=merge(rson[u],rson[v],mid+1,r);
        push_up(u);
        return u;
    }
    int query(int node,int l,int r,int ql,int qr){
        if(!node)return 0;
        if(ql<=l&&r<=qr)return tr[node];
        int mid=(l+r)>>1,ret=0;
        if(mid>=ql)ret+=query(ls,l,mid,ql,qr);
        if(mid+1<=qr)ret+=query(rs,mid+1,r,ql,qr);
        return ret;
    }
}before,seg;
int n,q,p[N],sz[N],st;
int pos[N];
ll ans[N];
vector<int> path;
vector<vector<int>> G;
vector<vector<Query>> Q1,Q2;
void dfs0(int x){
    sz[x]=1;
    for(auto to:G[x]){
        dfs0(to);
        sz[x]+=sz[to];
    }
}
void dfs1(int x){
    path.push_back(x);
    pos[x]=path.size()-1;
    tr.modify(x,sz[x]);
    for(auto itr:Q1[x]){
        tr.modify(x,sz[x]);
        int l=1,r=n,ret=-1;
        while(l<=r){
            int mid=(l+r)>>1;
            if(tr.query(mid)>=itr.k){
                r=mid-1;
                ret=mid;
            }else l=mid+1;
        }
        int dir=-1,k=itr.k-tr.query(ret-1);
        if(ret!=x)dir=path[pos[ret]+1];
        Q2[ret].push_back((Query){x,-1,ret,k,itr.idx,dir});
    }
    for(auto to:G[x]){
        tr.modify(x,sz[x]-sz[to]);
        dfs1(to);
    }
    tr.modify(x,0);
    path.pop_back();
}
void dfs2(int x){
    for(auto to:G[x])dfs2(to);
    for(auto to:G[x])before.rt[x]=before.merge(before.rt[x],before.rt[to],1,n);
    for(auto itr:Q2[x]){
        int l=1,r=n,ret=-1;
        while(l<=r){
            int mid=(l+r)>>1;
            int val=before.query(before.rt[x],1,n,1,mid);
            if(itr.dir!=-1)val-=seg.query(seg.rt[itr.dir],1,n,1,mid);
            if(val>=itr.k){
                ret=mid; 
                r=mid-1;
            }else l=mid+1;
        }
        ans[itr.idx]=(ll)n*n*(itr.x-1)+(ll)n*(itr.LCA-1)+(ret-1);
    }
    for(auto to:G[x])seg.rt[x]=seg.merge(seg.rt[x],seg.rt[to],1,n);
}
int main(){
    fastio;
    cin>>n>>q;
    tr.init(n);
    G=vector<vector<int>>(n+1);
    Q1=Q2=vector<vector<Query>>(n+1);
    rep(i,1,n){
        cin>>p[i];
        if(p[i]==0)st=i;
        else G[p[i]].push_back(i);
    }
    dfs0(st);
    rep(i,1,q){
        ll k;cin>>k;
        // k--;
        int x=(k+n-1)/n;
        k=(k-1)%n+1;
        Q1[x].push_back((Query){x,-1,-1,(int)k,i,-1});
    }
    dfs1(st);
    rep(i,1,n){
        before.modify(before.rt[i],1,n,i,1);
        seg.modify(seg.rt[i],1,n,i,1);
    }
    dfs2(st);
    rep(i,1,q)cout<<ans[i]<<endl;
    return 0;
}

标签:Championship,int,auto,mid,Pacific,ICPC,ret,LCA,itr
From: https://www.cnblogs.com/yoshinow2001/p/18052031

相关文章

  • The 2023 ICPC Asia Macau Regional Contest (The 2nd Universal Cup. Stage 15: Maca
    Preface最幽默的一集这场开局感觉三个人都有点发昏了,前3h30min就出了两个题,有种要打铁了的感觉后面想着干脆保个银牌稳扎稳打吧,没想到后面1h连着出了四个题成功冲到银首最后徐神好像也会G这个博弈了,如果前面不犯病的话感觉还真有机会出7题的说A.(-1,1)-Sumplete徐神基本被......
  • The 2023 ICPC Asia Jinan Regional Contest (The 2nd Universal Cup. Stage 17: Jina
    Preface趁着开学前最后一天再凑一场训练,今天这场手感不错前面的题都是一遍过最后靠着前期的手速7题苟进Au区,后面90min徐神B题没有Rush出来,思路啥都是对的就是一点细节没写好A.ManyManyHeads首先发现我们可以将所有相邻的同类型括号设为一对,这样一定能得出一个合法的串考......
  • SDNU_ACM_ICPC_2024_Winter_Practice_1st 赛后
    A:题目给出t个n,对每个n,令n=x+y+z,x|n,y|n,z|n,输出最大的xyz的值。解法打表找规律#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;intmain(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);intt;cin>>t;while(t--){......
  • P9847 [ICPC2021 Nanjing R] Crystalfly
    前景导入当\(t\in[1,2]\)时,本题如何求解?答:树形dp设\(f[i]\)为以\(i\)为根的树,根节点的晶蝶已消散且儿子节点的晶蝶还未被惊动,能获得的最大晶蝶数。则有状态转移方程\(f[i]=(\sumf[u])+max(a[u])\),其中\(u\)为\(i\)的儿子。最终的答案即为\(f[1]+a[1]\)划向更......
  • 2019-2020 ICPC Northwestern European Regional Programming Contest (NWERC 2019)
    Preface由于前两天疑似感染风寒,今天头痛+鼻塞+咽炎一条龙,硬打了4h顶不住就下班了最后过了8个题也还行,比较可惜的就是H题从中期写到结束,祁神和徐神各写了一个version也没过A.AverageRank挺有意思的一个题考虑将一个原来分数为\(x\)的人加\(1\)分后会发生什么,显然只有原来分......
  • 2020-2021 ICPC East Central North America Regional Contest (ECNA 2020)
    Preface队友C麻了我直接3h下班雀魂启动,如果时间多点感觉还有AK希望不过不得不说北美场难度都集中在模拟题上了,一般压轴都是数学或者几何,而这类题目遇到徐神祁神就是洒洒水了A.AllintheFamily出题人真是丧心病狂,不过这题只是看起来恶心实际写起来感觉还好做法本身由于树......
  • 2022-2023 ICPC East Central North America Regional Contest (ECNA 2022)
    Preface闲了两天没训练,今天又开始上班,结果唐得发昏后期也没题可写直接光速下班只能说感觉老外的题目难度跨度都好大,easy确实简单,hard确实难,medium确实少A.A-MazingPuzzle题目看起来很复杂,但仔细一想会发现有用的状态总数只有\(4n^2\)种即我们可以暴力记录下两个机器人的坐......
  • 2018-2019, ICPC, Asia Yokohama Regional Contest 2018
    Preface又被输出格式创飞了,E题狂暴卡1h后面发现原来输出边的时候没有按照一小一大的顺序来输出不过后面也没啥会的题了,几何、线代题做不来,对着一个四色定理题乱搞一波发现样例都过不去值得一提的是这场前期完全是顺序开题,从A一直开到EA.DigitsAreNotJustCharacters签到......
  • 2021-2022 ICPC Southwestern European Regional Contest (SWERC 2021-2022)
    Preface这场打的也挺好的,前中期稳定出题,后期还和徐神接力写了一个K最后乱猜还猜对了H题的结论,可惜因为常数以及三分的bound等问题赛事没过最后10题舒服下班A.OrganizingSWERC签到,话说外国场的A基本都是签到啊#include<cstdio>#include<iostream>#defineRIregisterin......
  • 2019-2020 ICPC Southwestern European Regional Programming Contest (SWERC 2019-20
    Preface这场总体打的不错,虽然最后RushL题失败,没有想到关键优化导致没卡过去有点可惜,但奈何徐神还是太C了最后10题下班,赛后祁神发现L关键优化10min改完就过了,同时赛中徐神也看出了E的做法,感觉这场时间充足甚至有AK的可能的说A.Environment-FriendlyTravel很典的一个题,不难......