文章目录
2019CCPC网络赛(部分题解)
1002 array(线段树)
题目链接
题意 :
给定一个 数组,每个元素都不相同,且都在 的范围之内,即是一个排列,要求实现两种操作
第一种操作是: ,把 的的值加上
第二种操作是: ,查询 大于等于 的且不与 之间元素值相同的最小数。
思路:
我们发现 而第一个操作是加上 那么就相当于将 从集合中去掉。我们考虑用线段树维护可以使用 的最大 ,线段树下标维护 ,值维护 。那么每一次查询就是在线段树中查询 区间中值大于等于 的下标,使用线段树上二分即可。
代码:
#include<bits/stdc++.h>
#define ls x<<1
#define rs x<<1|1
using namespace std;
const int N=1e5+10;
int a[N];
struct node{
int l,r,mx;
}e[N*4];
void up(int x){
e[x].mx=max(e[ls].mx,e[rs].mx);
}
void built(int x,int l,int r){
e[x].l=l;e[x].r=r;
if(l==r){
e[x].mx=0;
return ;
}
int mid=(l+r)/2;
built(ls,l,mid);built(rs,mid+1,r);up(x);
}
void add(int x,int pos,int v){
if(e[x].l==e[x].r){
e[x].mx=v;return ;
}
int mid=(e[x].l+e[x].r)/2;
if(pos<=mid)add(ls,pos,v);
else add(rs,pos,v);
up(x);
}
int query11(int x,int k){//二分
if(e[x].l==e[x].r)return e[x].l;
if(e[ls].mx>=k)return query11(ls,k);
else return query11(rs,k);
}
int query1(int x,int LL,int RR,int k){//划分区间
if(LL>RR)return -1;
if(e[x].l>RR||e[x].r<LL)return -1;
if(e[x].l>=LL&&e[x].r<=RR){
if(e[x].mx>=k)return query11(x,k);
else return -1;
}
int ans=query1(ls,LL,RR,k);
if(ans==-1)ans=query1(rs,LL,RR,k);
return ans;
}
int n,m,T;
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
built(1,1,n+1);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);add(1,a[i],i-1);
}
add(1,n+1,n+1);
int op,x,y,last=0;
for(int i=1;i<=m;i++){
scanf("%d",&op);
if(op==1){
scanf("%d",&x);x^=last;
add(1,a[x],n+1);//赋值成n+1
}else{
scanf("%d%d",&x,&y);
x^=last,y^=last;
int ans=query1(1,y,n+1,x);
//cout<<endl;
printf("%d\n",ans);
//cout<<endl;
last=ans;//强制在线
}
}
}
}
1003 K-th occurrence (后缀数组 + 主席树 + 二分)
题意:
就是给一个字符串 , ,有 次询问,每次询问 表示子串 第 次出现的位置。
思路:
据说是套路题,但是比赛的时候不会后缀数组,我们先将样例在纸上求出 然后对于一个 可以二分出在 的区间,对应到 数组中的第 大值即可。
要先预处理出 和 。
代码:
下面的代码会WA,调试了一个晚上没成功,不过大体思路是对的
#include<bits/stdc++.h>
#define ls x<<1
#define rs x<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+10;
int TT;
char S[N];
int s[N];
int n,q;
struct _SA{
int sa[N],t1[N],t2[N],c[N],rk[N],ht[N];
/*
void build(int s[],int n,int m){
int i,j,p,*x=t1,*y=t2;
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[i]=s[i]]++;
for(i=1;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
for(j=1;j<=n;j<<=1){
p=0;
for(i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=1;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<m;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
if(p>=n)break;
m=p;
}
}
*/
void init(){
for(int i=1;i<=n;i++)sa[i]=ht[i]=rk[i]=t1[i]=t2[i]=c[i]=0;
}
void build(int s[],int n,int m)
{
int i,j,p,*x=t1,*y=t2;
for(i=1;i<=m;i++)c[i]=0;
for(i=1;i<=n;i++)c[x[i]=s[i]]++;
for(i=2;i<=m;i++)c[i]+=c[i-1];
for(i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(j=1;j<=n;j<<=1)
{
p=0;
for(i=n-j+1;i<=n;i++)y[++p]=i;
for(i=1;i<=n;i++)if(sa[i]>j)y[++p]=sa[i]-j;
for(i=1;i<=m;i++)c[i]=0;
for(i=1;i<=n;i++)c[x[i]]++;
for(i=2;i<=m;i++)c[i]+=c[i-1];
for(i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
p=1;x[sa[1]]=1;
for(i=2;i<=n;i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j])?p:++p;
if(p==n)break;
m=p;
}
}
void getht(int s[],int n)
{
int i,j,k=0;
for(i=1;i<=n;i++)rk[sa[i]]=i;
for(i=1;i<=n;i++)
{
if(rk[i]==1)continue;
if(k)k--;
j=sa[rk[i]-1];
while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
ht[rk[i]]=k;
}
}
}SA;
/*
struct Tree{//一开始想用线段树维护最小值,果断T了
int l[N*4],r[N*4],mi[N*4];
void up(int x){
mi[x]=min(mi[ls],mi[rs]);
}
void built(int x,int L,int R){
l[x]=L;r[x]=R;
if(L==R){
mi[x]=SA.ht[L];
return ;
}
int mid=(L+R)/2;
built(ls,L,mid);built(rs,mid+1,R);
up(x);
}
int query(int x,int LL,int RR){
if(LL<=l[x]&&RR>=r[x]){
return mi[x];
}
int mid=(l[x]+r[x])/2;
int ans=inf;
if(LL<=mid)ans=min(ans,query(ls,LL,RR));
if(RR>mid)ans=min(ans,query(rs,LL,RR));
return ans;
}
}ST;
*/
int arr[N];
struct _ST{
int lg[N];int st[N][21];
void init(int n){
for(int i=1;i<=n;i++)
for(int j=0;j<=20;j++)st[i][j]=0;
lg[0]=-1;for(int i=1;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)arr[i]=SA.ht[i];
for(int i=1;i<=n;i++)st[i][0]=i;
for(int i=1;i<=20;i++){
for(int j=1;j<=(n+1-(1<<i));j++){
st[j][i]=arr[st[j][i-1]]<=arr[st[j+(1<<(i-1))][i-1]]?st[j][i-1]:st[j+(1<<(i-1))][i-1];
}
}
}
/*
int query(int L,int R){
int k=lg[R-L+1];
int id=arr[st[L][k]]<=arr[st[R-(1<<k)+1][k]]?st[L][k]:st[R-(1<<k)+1][k];
return arr[id];
}
*/
int query(int i,int j) {
if(i-1==j) return n-SA.sa[i-1]+1;
int x=log2(j-i+1);
return arr[min(st[i][x],st[j-(1<<x)+1][x])];
}
}ST;
struct node{
int l,r,sum;
}T[N*50];
int cnt=0;
int root[N];
void update(int l,int r,int &x,int y,int pos){
T[++cnt]=T[y];T[cnt].sum++;x=cnt;
if(l==r)return ;
int mid=(l+r)/2;
if(pos<=mid)update(l,mid,T[x].l,T[y].l,pos);
else update(mid+1,r,T[x].r,T[y].r,pos);
}
int query(int l,int r,int x,int y,int k){
if(l==r)return l;
int mid=(l+r)/2;int sum=T[T[y].l].sum-T[T[x].l].sum;
if(sum>=k)return query(l,mid,T[x].l,T[y].l,k);
else return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
int main()
{
scanf("%d",&TT);
while(TT--){
cnt=0;
scanf("%d%d",&n,&q);for(int i=0;i<=n+1;i++)root[i]=0;SA.init();
for(int i=1;i<=n+1;i++)T[i].l=T[i].r=T[i].sum=0;//初始化
scanf("%s",S+1);int mx=0;n=strlen(S+1);
for(int i=1;i<=n;i++){
s[i]=S[i]-'a'+1;
mx=max(s[i],mx);
}
SA.build(s,n,mx);
SA.getht(s,n);
for(int i=1;i<=n;i++)update(1,n,root[i],root[i-1],SA.sa[i]);
ST.init(n);
for(int i=1,a,b,k;i<=q;i++){
scanf("%d%d%d",&a,&b,&k);
int len=(b-a+1);
int x=SA.rk[a]; int L,R;
int l=1,r=x;
while(l<=r){
int mid=(l+r)/2;
if(ST.query(mid+1,x)>=len)r=mid-1,L=mid;
else l=mid+1;
}
l=x,r=n;
while(l<=r){
int mid=(l+r)/2;
if(ST.query(x+1,mid)>=len)l=mid+1,R=mid;
else r=mid-1;
}
//L--;
//cout<<L<<" "<<R<<endl;//找到能延伸的最大区间
if(R-L+1<k){puts("-1");continue;}//出现次数小于k次就输出-1
printf("%d\n",query(1,n,root[L-1],root[R],k));
}
}
}