1 条题解

  • 0
    @ 2025-8-24 22:27:08

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar tzc_wk
    **

    搬运于2025-08-24 22:27:08,当前版本为作者最后更新于2020-12-26 21:19:11,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题面传送门

    题意:

    给出集合 $S=[l_1,r_1]\cup[l_2,r_2]\cup[l_3,r_3]\cup\dots\cup[l_n,r_n]$ 和整数 kk,求有多少个三元组 (a,b,c)(a,b,c) 满足:

    • a,b,cSa,b,c\in Sa<b<ca<b<c

    • a,b,ca,b,c 两两异或得到的值均 k\leq k

    答案对 109+710^9+7 取模。

    1n2×1041\leq n\leq 2\times 10^4,$0\leq l_1\leq r_1\lt l_2\leq r_2\lt\dots\lt l_n\leq r_n\leq 10^9$

    Yet another 1e9+7

    Yet another 计数 dp

    Yet another 我做不出来的题

    阿巴细节题。

    首先考虑优雅的暴力,也就是 max(k,rn)106\max(k,r_n)\leq 10^6 那一档部分分。

    建一棵包含 SS 所有元素的 trie 树。

    dp1idp1_i 表示选择的三个数均在 ii 的子树中的方案数。假设我们当前考虑到从高到低的第 xx 位。

    可以分为四种情况转移:

    1. 选择的三个数均在 ii 的左子树内。这种情况又可分为两种情况:如果 kk 的从高到低的第 xx 位为 11,那么不论选哪三个点,它们两两异或起来从高到低第 xx 位都为 00。故不论选哪三个点都满足条件,贡献为 (sizchi,03)\dbinom{siz_{ch_{i,0}}}{3} ;如果 kk 从高到低的第 xx 位为 00,那么贡献就是 dp1chi,0dp1_{ch_{i,0}}
    2. 选择的三个数均在 ii 的右子树内,与第一种情况几乎一样。
    3. 选择的三个数中,其中两个数 a,ba,bii 的左子树内,一个数 ccii 的右子树内。由于 bcb\oplus c 从高到低的第 xx 位为 11,故这种情况只有当 kk 的从高到低的第 xx 位为 11 的情况下才能发生。
    4. 选择的三个数中,其中一个数 aaii 的左子树内,一个数 b,cb,cii 的右子树内,与第三种情况几乎一样。

    不难发现,在这四种情况中前两种情况都是可以直接转移的,而后两种无法直接表示出来,需要引入另一个状态。

    再设 dp2i,jdp2_{i,j} 表示选择的三个数中两个数在 ii 的子树中,一个在 jj 的子树中的方案数。

    继续分情况讨论,可以分为六种情况:

    1. ii 子树中的两个数都在 ii 的左子树中,在 jj 子树中的数在 jj 的左子树中。这种情况又可分为两种情况:如果 kk 的从高到低的第 xx 位为 11,那么不论选哪三个点,它们两两异或起来从高到低第 xx 位都为 00。故不论选哪三个点都满足条件,贡献为 (sizchi,02)×sizchj,0\dbinom{siz_{ch_{i,0}}}{2}\times siz_{ch_{j,0}}。 如果 kk 从高到低的第 xx 位为 00,那么贡献就是 dp2chi,0,chj,0dp2_{ch_{i,0},ch_{j,0}}

    2. ii 子树中的两个数 a,ba,b 都在 ii 的左子树中,在 jj 子树中的数 ccjj 的右子树中。由于 aca\oplus c 从高到低的第 xx 位为 11,所以这种情况只有在当 kk 的从高到低的第 xx 位为 11 的情况下才能发生。这种情况产生的贡献为 dp2chi,0,chj,1dp2_{ch_{i,0},ch_{j,1}}

    3. ii 子树中的两个数 a,ba,baaii 的左子树中,bbii 的右子树中,在 jj 子树中的数 ccjj 的左子树中。由于 bcb\oplus c 从高到低的第 xx 位为 11,所以这种情况只有在当 kk 的从高到低的第 xx 位为 11 的情况下才能发生。我们还注意到,不论 aa 为何值都有 ab<ac<bca\oplus b<a\oplus c<b\oplus c, 也就是说,不论 aa 取何值,只要 b,cb,c 满足条件,aa 一定满足条件。求出满足条件的 b,cb,c 个数后乘一个 sizchi,0siz_{ch_{i,0}} 就行了。

    4. ii 子树中的两个数 a,ba,baaii 的左子树中,bbii 的右子树中,在 jj 子树中的数 ccjj 的右子树中,与第三种情况几乎一样,只不过变成了求出满足条件的 a,ca,c 的个数 。

    5. ii 子树中的两个数 a,ba,b 都在 ii 的右子树中,在 jj 子树中的数 ccjj 的左子树中,与第二种情况几乎一样。

    6. ii 子树中的两个数都在 ii 的右子树中,在 jj 子树中的数在 jj 的右子树中,与第一种情况几乎一样。

    但是碰到这里我们又犯难了,情况 1,2,5,6 可以直接转移,但是情况 3,4 无法通过已有状态求出满足条件的 b,cb,c 的个数,这是我们又需要引入一个新状态。

    再设 dp3i,jdp3_{i,j} 表示两个数 a,ba,b 中一个数在 ii 的子树中,一个在 jj 的子树中的方案数。

    又可以分四种情况:

    1. aaii 的左子树中,bbjj 的左子树中,这种情况又可分为两种情况:如果 kk 的从高到低的第 xx 位为 11,那么不论选哪两个点,它们两两异或起来从高到低第 xx 位都为 00。故不论选哪三个点都满足条件,贡献为 sizchi,0×sizchj,0siz_{ch_{i,0}}\times siz_{ch_{j,0}}。 如果 kk 从高到低的第 xx 位为 00,那么贡献就是 dp3chi,0,chj,0dp3_{ch_{i,0},ch_{j,0}}

    2. aaii 的左子树中,bbjj 的右子树中。由于 aba\oplus b 从高到低的第 xx 位为 11,所以这种情况只有在当 kk 的从高到低的第 xx 位为 11 的情况下才能发生。

    3. aaii 的右子树中,bbjj 的左子树中,与第二种情况几乎一样。

    4. aaii 的右子树中,bbjj 的右子树中,与第一种情况几乎一样。

    算下时间复杂度:dp1dp1 的时间复杂度肯定是没问题的,加个记忆化每个 dp1idp1_i 最多被计算一次。关键是 dp2dp2dp3dp3dp2dp2dp3dp3 状态是二维的。可合法的状态数真的是 n2n^2 吗?非也。拿 dp2dp2 举例,只有当 kk 的第 xx 位为 00 的时候才会调用 dp2chi,0,chj,0dp2_{ch_{i,0},ch_{j,0}}dp2chi,1,chj,1dp2_{ch_{i,1},ch_{j,1}},当 kk 的第 xx 位为 11 的时候才会调用 dp2chi,0,chj,1dp2_{ch_{i,0},ch_{j,1}}dp2chi,1,chj,0dp2_{ch_{i,1},ch_{j,0}}。稍微观察下即可发现,iijj 表示的数异或起来肯定是 kk 的一个前缀。这意味着对于每个 ii 有唯一的 jj 与之对应,故合法状态数只有 O(m)\mathcal O(m),其中 mm 为 trie 数上的点数。故这个“优雅的暴力”是没问题的~~(真 nm 优雅)~~。

    最后考虑 k,rn109k,r_n\leq 10^9 的情况。其实想到这一步本题就已经做完了 80%80\% 了,虽然到这一步只包含了本题 40%40\% 的部分分。

    我们发现待插入的数很多,高达 10910^9,但是这些数都是一段一段区间,区间个数只有 2×1042\times 10^4。于是我们可以想到一个东西叫做线段树,可以通过线段树的思想将每个区间拆分成 log109\log 10^9 个长度为 22 的整数次幂的区间插入 trie 树。这样一来我们可以得到这样的 trie 树:trie 树上每个叶子节点代表一个大小为 2m2^m 的满二叉树。

    这样说有些抽象,举个例子,譬如我们要插入区间 [0,6][0,6],如果按照之前的暴力我们会这样插:

    但我们发现,黄色部分和绿色部分都是满二叉树,根本不用把它们建出来,所以我们索性把它们缩成一个“大点”:

    但是这样还是不太行啊?如果你 dfsdfs 到一个“大点”,对应到 dpdp 值怎么计算呢?

    可以考虑额外建 3131 个节点,编号为 0,1,2,,300,1,2,\dots,30,节点 ii 的左右儿子都是节点 i1i-1,这样大小为 2m2^m 的子树就等价于节点 mmdfsdfs 的时候直接在这 3131 个节点上记录 dpdp 值就可以了。

    代码不长,也就 100 行而已,不过细节实在是太太太太太太多了,这篇题解也写了整整 1 个小时。。。

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
    template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
    typedef pair<int,int> pii;
    typedef long long ll;
    template<typename T> void read(T &x){
       x=0;char c=getchar();T neg=1;
       while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
       while(isdigit(c)) x=x*10+c-'0',c=getchar();
       x*=neg;
    }
    const int MAXN=2e4;
    const int LOG_N=30;
    const int MAXT=1e6;
    const int MAX=(1<<LOG_N)-1;
    const int MOD=1e9+7;
    const int TWO=5e8+4;
    const int SIX=166666668;
    int n,k,siz[MAXT+5],ch[MAXT+5][2],ncnt=LOG_N+1,rt=LOG_N+1,dep[MAXT+5];
    bool isend[MAXT+5];
    void update(int &k,int l,int r,int nl,int nr,int d){
       if(l==nl&&nr==r){k=LOG_N-d;return;}
       if(!~k) k=++ncnt,dep[k]=d;int mid=(l+r)>>1;
    //	printf("%d %d %d %d %d %d\n",k,l,r,nl,nr,d);
       if(nr<=mid) update(ch[k][0],l,mid,nl,nr,d+1);
       else if(nl>mid) update(ch[k][1],mid+1,r,nl,nr,d+1);
       else update(ch[k][0],l,mid,nl,mid,d+1),update(ch[k][1],mid+1,r,mid+1,nr,d+1);
       siz[k]=siz[ch[k][0]]+siz[ch[k][1]];
    }
    int dp1[MAXT+5];
    map<int,int> dp2[MAXT+5],dp3[MAXT+5];
    int calc1(int x);
    int calc2(int x,int y);
    int calc3(int x,int y);
    //calc1: 3 nodes in subtree of x
    //calc2: 2 nodes in subtree of x and 1 node in subtree of y
    //calc3: 1 nodes in subtree of x and 1 node in subtree of y
    int calc1(int x){
    //	if(x==0||x==1) return 0;
       if(~dp1[x]) return dp1[x];
       dp1[x]=0;
       if(~ch[x][0]){
       	if(k>>(LOG_N-dep[x]-1)&1) dp1[x]=(dp1[x]+1ll*siz[ch[x][0]]*(siz[ch[x][0]]-1)%MOD*(siz[ch[x][0]]-2)%MOD*SIX%MOD)%MOD;
       	else dp1[x]=(dp1[x]+calc1(ch[x][0]))%MOD;
       }
       if(~ch[x][1]){
       	if(k>>(LOG_N-dep[x]-1)&1) dp1[x]=(dp1[x]+1ll*siz[ch[x][1]]*(siz[ch[x][1]]-1)%MOD*(siz[ch[x][1]]-2)%MOD*SIX%MOD)%MOD;
       	else dp1[x]=(dp1[x]+calc1(ch[x][1]))%MOD;
       }
       if(~ch[x][0]&&~ch[x][1]) if(k>>(LOG_N-dep[x]-1)&1){
       	dp1[x]=(dp1[x]+calc2(ch[x][0],ch[x][1]))%MOD;
       	dp1[x]=(dp1[x]+calc2(ch[x][1],ch[x][0]))%MOD;
       }
    //	printf("%d %d\n",x,dp1[x]);
       return dp1[x];
    }
    int calc2(int x,int y){
    //	if(!x) return 0;
       if(dp2[x].find(y)!=dp2[x].end()) return dp2[x][y];
       dp2[x][y]=0;
       if(~ch[x][0]&&~ch[y][0]){
       	if(k>>(LOG_N-dep[x]-1)&1)
       		dp2[x][y]=(dp2[x][y]+1ll*siz[ch[x][0]]*(siz[ch[x][0]]-1)%MOD*TWO%MOD*siz[ch[y][0]]%MOD)%MOD;
       	else dp2[x][y]=(dp2[x][y]+calc2(ch[x][0],ch[y][0]))%MOD;
       }
       if(~ch[x][0]&&~ch[y][1]) if(k>>(LOG_N-dep[x]-1)&1) dp2[x][y]=(dp2[x][y]+calc2(ch[x][0],ch[y][1]))%MOD;
       if(~ch[x][0]&&~ch[x][1]&&~ch[y][0]) if(k>>(LOG_N-dep[x]-1)&1) dp2[x][y]=(dp2[x][y]+1ll*calc3(ch[x][1],ch[y][0])*siz[ch[x][0]]%MOD)%MOD;
       if(~ch[x][0]&&~ch[x][1]&&~ch[y][1]) if(k>>(LOG_N-dep[x]-1)&1) dp2[x][y]=(dp2[x][y]+1ll*calc3(ch[x][0],ch[y][1])*siz[ch[x][1]]%MOD)%MOD;
       if(~ch[x][1]&&~ch[y][0]) if(k>>(LOG_N-dep[x]-1)&1) dp2[x][y]=(dp2[x][y]+calc2(ch[x][1],ch[y][0]))%MOD;
       if(~ch[x][1]&&~ch[y][1]){
       	if(k>>(LOG_N-dep[x]-1)&1)
       		dp2[x][y]=(dp2[x][y]+1ll*siz[ch[x][1]]*(siz[ch[x][1]]-1)%MOD*TWO%MOD*siz[ch[y][1]]%MOD)%MOD;
       	else dp2[x][y]=(dp2[x][y]+calc2(ch[x][1],ch[y][1]))%MOD;
       }
    //	printf("c2 %d %d %d\n",x,y,dp2[x][y]);
       return dp2[x][y];
    }
    int calc3(int x,int y){
       if(!x) return 1;
       if(dp3[x].find(y)!=dp3[x].end()) return dp3[x][y];
       dp3[x][y]=0;
       if(~ch[x][0]&&~ch[y][0]){
       	if(k>>(LOG_N-dep[x]-1)&1) dp3[x][y]=(dp3[x][y]+1ll*siz[ch[x][0]]*siz[ch[y][0]]%MOD)%MOD;
       	else dp3[x][y]=(dp3[x][y]+calc3(ch[x][0],ch[y][0]))%MOD;
       }
       if(~ch[x][0]&&~ch[y][1]) if(k>>(LOG_N-dep[x]-1)&1) dp3[x][y]=(dp3[x][y]+calc3(ch[x][0],ch[y][1]))%MOD;
       if(~ch[x][1]&&~ch[y][0]) if(k>>(LOG_N-dep[x]-1)&1) dp3[x][y]=(dp3[x][y]+calc3(ch[x][1],ch[y][0]))%MOD;
       if(~ch[x][1]&&~ch[y][1]){
       	if(k>>(LOG_N-dep[x]-1)&1) dp3[x][y]=(dp3[x][y]+1ll*siz[ch[x][1]]*siz[ch[y][1]]%MOD)%MOD;
       	else dp3[x][y]=(dp3[x][y]+calc3(ch[x][1],ch[y][1]))%MOD;
       }
    //	printf("c3 %d %d %d\n",x,y,dp3[x][y]);
       return dp3[x][y];
    }
    int main(){
       fill1(ch);
       siz[0]=1;for(int i=1;i<=LOG_N;i++) ch[i][0]=ch[i][1]=i-1,siz[i]=(1<<i),dep[i]=LOG_N-i;
       memset(dp1,-1,sizeof(dp1));scanf("%d%d",&n,&k);
       for(int i=1;i<=n;i++){int l,r;scanf("%d%d",&l,&r);update(rt,0,MAX,l,r,0);}
    //	for(int i=0;i<=ncnt;i++) printf("%d %d %d %d\n",ch[i][0],ch[i][1],dep[i],siz[i]);
       printf("%d\n",calc1(rt));
       return 0;
    }
    /*
    1 3
    0 3
    
    1 6
    0 5
    
    1 15
    0 10
    */
    
    • 1

    信息

    ID
    6346
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者