HDU 6096 树套树

思路:

网上的题解有AC自动机的,有trie树的,还有(乱搞?)的

首先把输入的那n个串按照字典序排序,

把n个串翻转以后再按照字典序排序

这样我们发现, 查的前缀在字典序排序后是一段区间,

查的后缀翻转一下在翻转后的字典序排序以后也是一段区间

这样如果不考虑重叠的问题,就是一个简单的二维数点问题,一维排序,一维线段树即可解决

如果有重叠的问题,我们需要搞出来每个字符串的长度,使给出的前缀长+后缀长>=原字符串长度

此时题目变成了三维偏序,排序后树套树即可。

//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
const int N=500050;
int cases,n,q,root[N<<3],cnt,tree[N*150],lson[N*150],rson[N*150];
struct Node{string str;int id,len;}X[N],Y[N],tmp;
bool operator<(Node a,Node b){return a.str<b.str;}
struct Pnt{int x,y,z;}p[N];
struct Ask{int xl,xr,yl,yr,z,id,ans;}ask[N];
bool cmp1(Pnt a,Pnt b){return a.z>b.z;}
bool cmp2(Ask a,Ask b){return a.z>b.z;}
bool cmp3(Ask a,Ask b){return a.id<b.id;}
void Insert(int l,int r,int &pos,int wei){
    if(!pos)pos=++cnt;tree[pos]++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(mid>=wei)Insert(l,mid,lson[pos],wei);
    else Insert(mid+1,r,rson[pos],wei);
}
void insert(int l,int r,int pos,int num,int wei){
    Insert(1,n,root[pos],wei);
    if(l==r)return;
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(mid<num)insert(mid+1,r,rson,num,wei);
    else insert(l,mid,lson,num,wei);
}
int Query(int l,int r,int pos,int L,int R){
    if(l>=L&&r<=R)return tree[pos];
    int mid=(l+r)>>1;
    if(mid<L)return Query(mid+1,r,rson[pos],L,R);
    else if(mid>=R)return Query(l,mid,lson[pos],L,R);
    else return Query(l,mid,lson[pos],L,R)+Query(mid+1,r,rson[pos],L,R);
}
int query(int l,int r,int pos,int xl,int xr,int yl,int yr){
    if(l>=xl&&r<=xr)return Query(1,n,root[pos],yl,yr);
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(mid<xl)return query(mid+1,r,rson,xl,xr,yl,yr);
    else if(mid>=xr)return query(l,mid,lson,xl,xr,yl,yr);
    else return query(l,mid,lson,xl,xr,yl,yr)+query(mid+1,r,rson,xl,xr,yl,yr);
}
int main(){
    ios::sync_with_stdio(false);
    cin>>cases;
    while(cases--){
        memset(tree,0,sizeof(int)*(cnt+5));
        memset(lson,0,sizeof(int)*(cnt+5));
        memset(rson,0,sizeof(int)*(cnt+5));
        memset(root,0,sizeof(root));cnt=0;
        cin>>n>>q;
        for(int i=1;i<=n;i++)cin>>X[i].str;
        sort(X+1,X+1+n);
        for(int i=1;i<=n;i++)X[i].id=i,X[i].len=X[i].str.length();
        for(int i=1;i<=n;i++)Y[i]=X[i];
        for(int i=1;i<=n;i++)reverse(Y[i].str.begin(),Y[i].str.end());
        sort(Y+1,Y+1+n);
        for(int i=1;i<=n;i++)p[i].x=Y[i].id,p[i].y=i,p[i].z=Y[i].len;
        for(int i=1;i<=q;i++){
            string pre,suf;cin>>pre>>suf;
            ask[i].id=i;tmp.str=pre;
            ask[i].xl=lower_bound(X+1,X+1+n,tmp)-X;
            tmp.str=pre,tmp.str.push_back(‘z‘+1);
            ask[i].xr=lower_bound(X+1,X+1+n,tmp)-X-1;
            tmp.str=suf,reverse(tmp.str.begin(),tmp.str.end());
            ask[i].yl=lower_bound(Y+1,Y+1+n,tmp)-Y;
            tmp.str.push_back(‘z‘+1);
            ask[i].yr=lower_bound(Y+1,Y+1+n,tmp)-Y-1;
            ask[i].z=pre.size()+suf.size();
        }
        sort(p+1,p+1+n,cmp1);sort(ask+1,ask+1+q,cmp2);
        int tt=1;
        for(int i=1;i<=q;i++){
            while(p[tt].z>=ask[i].z&&tt<=n)insert(1,n,1,p[tt].x,p[tt].y),tt++;
            if(ask[i].xl>ask[i].xr||ask[i].yl>ask[i].yr){ask[i].ans=0;continue;}
            ask[i].ans=query(1,n,1,ask[i].xl,ask[i].xr,ask[i].yl,ask[i].yr);
        }sort(ask+1,ask+1+q,cmp3);
        for(int i=1;i<=q;i++)cout<<ask[i].ans<<endl;
    }
}

原文地址:https://www.cnblogs.com/SiriusRen/p/9424836.html

时间: 2024-11-14 13:18:50

HDU 6096 树套树的相关文章

hdu 4417 Super Mario/树套树

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意很简单,给定一个序列求一个区间 [L, R,]中小于等于H的元素的个数. 好像函数式线段树可解吧,可弱弱的沙茶一直没弄懂其精髓,只好用树套树暴力碾压了 额树套树,线段树的每一个节点套一个sb树. 当查询[l,r]区间中的值小于等于H的个数,先用线段树找到相应的区间, 然后再查询该区间下对应的平衡树中小于等于H的个数,累加即可. 一直以为会超时,结果400+ms就过了,数据应该很弱吧(自己对

HDU HDOJ5412(树套树

题目:要求支持带修改维护区间第k大的值.所谓的动态区间第k大. 思路:题解说的是树状数组套treap,然而没想通树状数组怎么维护...线段树的话就是把所有的值离散化一下,离线建个关于值的线段树,每个节点是一个treap,treap里的值用位置做关键字,然后做区间查询,复杂度是O(nlogn*logn).基本也是经典的树套树做法....然后赛后写了两遍都没过.....今天心血来潮再挑战一下,结果从8点调到晚上1点.其间各种爆内存各种re各种t.....随机数据对拍了好久没拍出问题来....然后一直

BZOJ_3196_二逼平衡树(树套树:线段树+Treap)

描述 可以处理区间问题的平衡树. 分析 树套树.可以用线段树套Treap.人生第一道树套树的题... op1:如果在整区间,直接在该区间的treap上求解.否则分两个区间求解,然后相加.最后+1. op2:这个不太好直接做,可以二分,每次假定一个值,用这个值去做op1,以此求得一个rank=k+1的数,求rank=k的数等价与求这个数的前驱pre. op3:先删后加. op4&op5:如果在整区间,直接在该区间的treap上求解,否则分量个区间求解,pre取最大值,suc取最小值.注意有些数在有

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

[BZOJ3110] [Zjoi2013] K大数查询 (树套树)

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 Sample Output 1 2 1 HINT [

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample Input 5 4 1 5 3

BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)

Orz黄学长,蒟蒻在黄学长的带领下,通过阅读黄学长的代码!终于会了这道题! 首先我想先说一下这道题的思路(准确来说是黄学长的). 很明显,树状数组应该不用讲吧!关键是内存怎么开,维护一些什么样的数据? 其实我们通过观察,很快可以发现,你维护被删的数比维护所有的数轻松多了(不管是空间上,还是时间上).所以我们就可以从这方面想!(其实我一开始的思路,因为这道题我已经看过很久了,一直想写,毕竟是白书里面的一道例题嘛!一开始,蒟蒻的我是打算这样的用树状数组套权值线段树,并且是维护所有的数,我发现空间不够

bzoj 1901: Zju2112 Dynamic Rankings(树套树)

1901: Zju2112 Dynamic Rankings 经典的带修改求区间第k小值问题 树套树模板,我是用的线段树套splay实现的,而且用的数组模拟的,所以可能空间略大,bzoj过了,zoj过不了. 思路很简单,用线段树维护区间,用splay维护区间内的权值,然后询问的时候,二分答案key,然后在区间内找小于key的数有多少个. 贴上模板: #include<stdio.h> #include<string.h> #include<algorithm> #def

POJ 1195 2维线段树(树套树实现) 树状数组

1: #include <stdio.h> 2: #include <string.h> 3: #include <stdlib.h> 4: #include <algorithm> 5: #include <iostream> 6: using namespace std; 7:   8: #define LL(a) a<<1 9: #define RR(a) a<<1|1 10: const int MaxL = 10