博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷P5211 [ZJOI2017]字符串(线段树+乱搞)
阅读量:5933 次
发布时间:2019-06-19

本文共 3664 字,大约阅读时间需要 12 分钟。

题面

题解

为什么大佬们全都是乱搞的……莫非这就是传说中的暴力能进队,乱搞能AC……

似乎有位大佬能有纯暴力+玄学优化\(AC\)(不算上\(uoj\)\(Hack\)数据的话……这要是放到考场上就是切题的啊……)

整体思路呢,就是我们开一个线段树,线段树上的每一个区间维护“以这个区间右端点为结尾有可能成为后缀最小值的位置”

怎么合并呢

首先右儿子的所有节点都是可以加入的,因为它们后面也没有加上什么东西

然后对于左儿子来说它们相当于后面被整体怼了一个串,我们就要考虑它们是不是仍有可能成为后缀最小值了

首先比较两个位置的字典序大小,我们记\(u\)表示\(s[u,r]\)\(v\)表示\(s[v,r]\),假设\(u\)\(v\)长,如果\(v\)不是\(u\)的前缀那么它们已经比较出了字典序大小,两个中肯定可以扔掉一个

然而如果\(v\)\(u\)的前缀,事情就会变得比较辣手了

先给出结论:如果\(v\)\(u\)的前缀,\(v\)就没有用了

为啥嘞?

因为它们都来自当前区间的左儿子,那么\(v\)的长度最短是\(r-mid+1\)\(u\)的长度最长是\(r-l+1\),根据我们正常线段树的写法\(v\)的长度绝对大于\(u\)的长度的一半

因为\(v\)的长度大于\(u\)的一半,同时\(v\)既是\(u\)的前缀也是它的后缀,那么我们发现\(u\)必定是一个有周期串,而且右儿子中至少有一个周期的开头\(w\)

所以现在\(w\)的字典序比它更小

那么加上新的字符之后\(v\)有没有可能变成后缀最小值呢?

答案是否定的,如果加的字符就是周期串上该有的字符,\(w\)\(v\)小,如果加的字符小于该有的字符,\(w\)还是比\(v\)小,如果加的字符大于该有的字符,\(u\)就比\(v\)小,所以\(v\)无论如何都不可能再作为后缀最小值了

也就是说这个区间会把右儿子全都加上来,左端点最多加一个,那么一个节点上存的位置最多不会超过\(O(\log n)\)

所以我们只需要能在线段树上维护区间加以及判断字典序就行了

先说正解吧,判断字典序可以二分加哈希找到最后一个相等的位置再判断下一位的大小,不过我们要在区间加的状况下维护哈希值很困难,这样的话我们得把哈希值分块,一次修改就是\(O(\sqrt{n})\)

正解的代码……去看巨巨的吧……才不是因为太长了不想打呢

于是这里区间加直接暴力,判断字典序也直接暴力……似乎是因为数据太水所以可以过……

而且我看了看\(uoj\)上这题排名前几的好像全都是打暴力的……跑得比正解快啊……

代码抄kcz的

//minamoto#include
#define R register#define fp(i,a,b) for(R int i=(a),I=(b)+1;i
I;--i)#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)using namespace std;char buf[1<<21],*p1=buf,*p2=buf;inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}int read(){ R int res,f=1;R char ch; while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1); for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0'); return res*f;}inline int getop(){R char ch;while((ch=getc())>'9'||ch<'0');return ch-'0';}char sr[1<<21],z[20];int C=-1,Z=0;inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}void print(R int x){ if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x; while(z[++Z]=x%10+48,x/=10); while(sr[++C]=z[Z],--Z);sr[++C]='\n';}const int N=2e5+5;int s[N],n,q;inline int cmp(const int &a,const int &b,const int &r){ fp(i,0,r-max(a,b))if(s[a+i]!=s[b+i])return s[a+i]
cnt)pos[i]=rc->pos[i]; cnt=rc->cnt; fp(i,1,lc->cnt) while(233){ int t=cmp(lc->pos[i],pos[cnt],r); if(t>0)break; if(!t){ (((r-pos[cnt]+1)<<1)>r-lc->pos[i]+1)?--cnt:0, pos[++cnt]=lc->pos[i]; break; } if(!--cnt){ pos[++cnt]=lc->pos[i]; break; } } }}pool[N<<2],*rt;int tot,ql,qr,res,d;inline node* newnode(){return &pool[tot++];}void build(node* &p,int l,int r){ p=newnode(),p->l=l,p->r=r; if(l==r)return p->ins(l),void(); int mid=(l+r)>>1; build(p->lc,l,mid),build(p->rc,mid+1,r); p->upd();}void update(node* p){ if(ql<=p->l&&qr>=p->r)return; if(ql<=p->lc->r)update(p->lc); if(qr>p->lc->r)update(p->rc); p->upd();}void query(node* p){ if(ql<=p->l&&qr>=p->r){ R int i=1;!res?res=p->pos[i++]:0; for(;i<=p->cnt;++i)cmp(p->pos[i],res,qr)<0?res=p->pos[i]:0; return; } if(qr>p->lc->r)query(p->rc); if(ql<=p->lc->r)query(p->lc);}int main(){// freopen("testdata.in","r",stdin);// freopen("qwq.out","w",stdout); n=read(),q=read(); fp(i,1,n)s[i]=read(); build(rt,1,n); while(q--)if(getop()&1){ ql=read(),qr=read(),d=read(); if(qr-ql+1<=(n>>1))fp(i,ql,qr)s[i]+=d; else{ fp(i,1,ql-1)s[i]-=d; fp(i,qr+1,n)s[i]-=d; } update(rt); }else{ ql=read(),qr=read(),res=0; query(rt),print(res); } return Ot(),0;}

转载于:https://www.cnblogs.com/bztMinamoto/p/10562789.html

你可能感兴趣的文章
开源数据汇集工具
查看>>
几本自然语言处理入门书
查看>>
java根据本地Ip获取mac地址
查看>>
cocos2d-x路~使得第一个字游戏(一个)
查看>>
SQLServer:什么是主键(PK)和外键(FK)?
查看>>
Android开发之获取设备的屏幕信息和px dp之间的转换
查看>>
.NET中的动态编译
查看>>
Android开发UI之Action Bar
查看>>
在Oracle中使用Guid
查看>>
iOS 在不添加库的情况下 通过抽象类来获取自己想要的方法
查看>>
罗将公布手机锤,我感到深深的内疚
查看>>
spark(1.1) mllib 源代码分析
查看>>
CentOSserverMysql主从复制集群结构
查看>>
Android实例-设置消息提醒(XE8+小米2)
查看>>
CSS之设置滚动条样式
查看>>
Activity启动模式 及 Intent Flags 与 栈 的关联分析
查看>>
Raspberry Pi Kernel Compilation 内核编译官方文档
查看>>
Jquery 数组操作
查看>>
不少专车司机考虑退出
查看>>
【Raspberry Pi】openwrt 路由
查看>>