首页 > 其他分享 >NFLS NOI模拟 树数术

NFLS NOI模拟 树数术

时间:2024-05-22 23:30:50浏览次数:13  
标签:nxt ch NOI st char 序列 NFLS 数术 getchar

涉及知识点:树、倍增、单调栈

题意

给你一颗有 \(n\ (\leq 7\times10^5)\) 个节点的树,再给你一个长为 \(m\ (\leq7\times10^5)\) 的序列,序列中的值代表树上点的编号,有 \(q\) 次询问,每次询问取原序列的 \(k\ (\sum k\leq7\times10^5)\) 个子区间并连起来成为一段新的序列,问新的序列中有多少个点 \(i\) 满足 \(\forall j\in [1,i]\),\(a_j\) 在 \(a_i\) 的子树内。

思路

我们记 \(f(i)=k\) 为 \(i\) 后面出现的第一个 \(k\) 满足 \(a_{i\sim k}\) 均在 \(a_k\) 的子树内,那么我们可以在原序列上跳下一个 \(f(i)\) 不断查询,记 nxt[i][j] 为 \(i\) 跳 \(2^j\) 次的点。对于第一个区间 \([l_1,r_1]\),直接用倍增跳到第一个大于 \(r_1\) 的地方;对于第二及以后的区间 \([l_i,r_i]\),从包含前面所有区间的祖先开始跳,直到跳出 \(r_i\),而这样“包含前面所有区间的祖先”一定也是之前上一个区间结尾的点的后继,所以起点也可以直接从前面跳过来找。

思维不是非常复杂,就是跳的时候要仔细一点,具体实现看代码。

代码

#include<bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar __getchar
inline char __getchar(){
    static char ch[1<<20],*l,*r;
    return (l==r&&(r=(l=ch)+fread(ch,1,1<<20,stdin),l==r))?EOF:*l++;
}
#endif
template<class T>inline void rd(T &x){
    T res=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while('0'<=ch && ch<='9'){res=res*10+ch-'0';ch=getchar();}
    x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){
    static char wtbuff[20];
    static int wtptr;
    if(x==0){
        putchar('0');
    }
    else{
        if(x<0){x=-x;putchar('-');}
        wtptr=0;
        while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}
        while(wtptr--) putchar(wtbuff[wtptr]);
    }
    if(endch!='\0') putchar(endch);
}
typedef long long LL;
const int MAXN=7e5+5,MAXB=21;
int n,m,q,k,arr[MAXN],ecnt=0,head[MAXN];
int dfnmin[MAXN],dfnmax[MAXN],dfncnt=0;//dfnmin子树最小点 dfnmax子树最大点 dfn在两者之间说明在子树内
int nxt[MAXN][MAXB],nxtl[MAXN][MAXB],nxtr[MAXN][MAXB],lg[MAXN];
LL ans;
struct EDGE{
    int v,nxt;
}e[MAXN<<1];
inline void addedge(const int& u,const int& v){
    e[++ecnt].v=v;
    e[ecnt].nxt=head[u];
    head[u]=ecnt;
}
inline bool check(int id,int l,int r){
    // assert(l<=r);
    return dfnmin[arr[id]]<=l && r<=dfnmax[arr[id]];
}
void dfs(int x,int fa){
    dfnmin[x]=++dfncnt;
    for(int i=head[x];i;i=e[i].nxt){
        if(e[i].v==fa) continue;
        dfs(e[i].v,x);
    }
    dfnmax[x]=dfncnt;
}
int main(){
    // freopen("tree.in","r",stdin);
    // freopen("tree.out","w",stdout);
    rd(n);rd(m);rd(q);
    lg[1]=0;
    for(int i=2;i<=m;i++){
        lg[i]=lg[i/2]+1;
    }
    for(int i=1,u,v;i<n;i++){
        rd(u);rd(v);
        addedge(u,v);addedge(v,u);
    }
    dfs(1,-1);
    for(int i=1;i<=m;i++){
        rd(arr[i]);
    }
    nxt[m+1][0]=m+1;
    stack<int>st;
    for(int i=m;i>=1;i--){
        //单调栈找后面第一个全在子树内的区间
        while(!st.empty() && !check(st.top(),dfnmin[arr[i]],dfnmax[arr[i]])) st.pop();
        if(!st.empty()) nxt[i][0]=st.top();
        else nxt[i][0]=m+1;
        nxtl[i][0]=dfnmin[arr[i]];nxtr[i][0]=dfnmax[arr[i]];
        st.push(i);
    }
    for(int j=1;j<MAXB;j++){
        for(int i=1;i<=m+1;i++){
            nxt[i][j]=nxt[nxt[i][j-1]][j-1];//nxt: i跳j次的点
            if(i+(1<<(j-1))<=m) nxtl[i][j]=min(nxtl[i][j-1],nxtl[i+(1<<(j-1))][j-1]);
            else nxtl[i][j]=nxtl[i][j-1];
            if(i+(1<<(j-1))<=m) nxtr[i][j]=max(nxtr[i][j-1],nxtr[i+(1<<(j-1))][j-1]);
            else nxtr[i][j]=nxtr[i][j-1];
            //nxtl nxtr: i跳j次一共覆盖了哪些点
        }
    }
    while(q--){
        ans=0;
        int l,r,ptr,lall=dfncnt+1,rall=0;
        rd(k);
        while(k--){
            rd(l);rd(r);ptr=l;
            if(!check(ptr,lall,rall)){//跳起点
                for(int i=lg[m];i>=0;i--){
                    if(nxt[ptr][i]<=r && !check(nxt[ptr][i],lall,rall)) ptr=nxt[ptr][i];
                }
                ptr=nxt[ptr][0];
            }
            if(ptr<=r){//跳终点(跳出r)
                ans++;
                for(int i=lg[m];i>=0;i--){
                    if(nxt[ptr][i]<=r) ptr=nxt[ptr][i],ans+=(1<<i);
                }
            }
            int lglen=lg[r-l+1];
            lall=min(lall,min(nxtl[l][lglen],nxtl[r-(1<<lglen)+1][lglen]));
            rall=max(rall,max(nxtr[l][lglen],nxtr[r-(1<<lglen)+1][lglen]));
            //lall rall: 更新前面所有区间的覆盖的点
        }
        wt(ans,'\n');
    }
    return 0;
}

标签:nxt,ch,NOI,st,char,序列,NFLS,数术,getchar
From: https://www.cnblogs.com/SkyNet-PKN/p/18207368

相关文章

  • CSP历年复赛题-P1044 [NOIP2003 普及组] 栈
    原题链接:https://www.luogu.com.cn/problem/P1044题意解读:一组数入栈、出栈的方案数,如果了解卡特兰数,此题可以秒杀;如果不了解,也可以通过递归或者递推来解决;最次,可以通过DFS暴搜出方案数,当然对于n个数,一共有n次入栈、n次出栈,一共2n次,每次要么入栈要么出栈,总搜索次数在22n规模,n最......
  • CSP历年复赛题-P1045 [NOIP2003 普及组] 麦森数
    原题链接:https://www.luogu.com.cn/problem/P1045题意解读:要计算2p-1的位数和最后500位,实际上只需要计算2p,两者位数一致,前者比后者个位减1即可,且个位肯定不会是0,比较容易处理。解题思路:如果直接采用高精度乘法计算2p,p最大3.1*106,高精度所用数组最长大概9*105,一共最多计算3.......
  • CSP历年复赛题-P1043 [NOIP2003 普及组] 数字游戏
    原题链接:https://www.luogu.com.cn/problem/P1043题意解读:将n个环形数分成任意m组,组内求和再%10、负数转正,组间相乘,求所有分组方案中得到结果的最小值和最大值。解题思路:比赛题的首要目的是上分!此题一看就是DP,但是苦苦思索了半天,想不清楚状态表示,那么可以换换策略,先暴力得分再......
  • loj#575. 「LibreOJ NOI Round #2」不等关系
    记事件\(A\)为「当\(s_i=\texttt<\)时\(p_i<p_{i+1}\)」,事件\(B\)为「当\(s_i=\texttt<\)时\(p_i<p_{i+1}\),且存在\(s_j=\texttt>\),满足\(p_i<p_{i+1}\)。所求即\(n(A)-n(B)\)。\(n(A)\)是好求的,相当于部分定序排列,记每个递增段的长度为\(a_1......
  • CSP历年复赛题-P1037 [NOIP2002 普及组] 产生数
    原题链接:https://www.luogu.com.cn/problem/P1037题意解读:一个长整数,有若干数字替换规则,计算可以转换成多少种不同的整数。解题思路:看题之后,第一感觉,是用DFS:1、用字符串存储整数2、用领接表存储数字替换规则,因为一个数字可以替换成多个其他数字3、在dfs中,枚举字符串每个数字......
  • CSP历年复赛题-P1002 [NOIP2002 普及组] 过河卒
    原题链接:https://www.luogu.com.cn/problem/P1002题意解读:从A(0,0)点走到B(n,m)点,只能向右或者向下,C点以及其控制点不能走。解题思路:根据题意,此题要么递归(DFS),要么递推(动态规划)先分析数据规模,最大从起点到终点要走40步,每个步有2种走法,一共240种路径,DFS会超时,且方案数必须用longlong......
  • CSP历年复赛题-P1042 [NOIP2003 普及组] 乒乓球
    原题链接:https://www.luogu.com.cn/problem/P1042题意解读:分别针对11分制和21分制,输出每局比分。只需要判断一局的结束条件:得分高者如果达到11或者21,且比分间隔大于等于2分,则表示一局结束,可开始下一局,用模拟法即可解决。100分代码:#include<bits/stdc++.h>usingnamespaces......
  • [NOIP2000 提高组] 单词接龙
    传送锚点:https://www.luogu.com.cn/problem/P1019题目描述单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如beast和......
  • CSP历年复赛题-P1049 [NOIP2001 普及组] 装箱问题
    原题链接:https://www.luogu.com.cn/problem/P1049题意解读:装尽可能多的物品,使得总体积越大越好,即剩余空间最小,还是一个01背包问题,物品的体积就是其价值。解题思路:01背包模版题,物品体积、价值相同,直接采用一维dp。100分代码:#include<bits/stdc++.h>usingnamespacestd;co......
  • CSP历年复赛题-P1035 [NOIP2002 普及组] 级数求和
    原题链接:https://www.luogu.com.cn/problem/P1035题意解读:根据公式模拟法求解即可。解题思路:枚举i,计算sum,如果sum>k,则输出i100分代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){intk;cin>>k;doublesum=0;inti=0;while(......