对拍
while true; do
./data_maker>tmp.in #出数据
./MyProg<tmp.in>tmp.out #被测程序
./StdProg<tmp.in>tmp2.out #正确(暴力)程序
if diff tmp.out tmp2.out; then #比较两个输出文件
printf "AC\n" #结果相同显示AC
elif diff -B -b tmp.out tmp2.out; then
printf "PE\n"
else
printf "WA\n" #结果不同显示WA,并退出
exit 0
fi
done
搜索
DLX
精确覆盖
///n rows m columns
using namespace std;
const int maxnode = 100010;
const int MaxM = 1010;
const int MaxN = 1010;
struct DLX
{
int n,m,size;
int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
int H[MaxN], S[MaxM];
int ansd, ans[MaxN];
void init(int _n,int _m)
{
n = _n;
m = _m;
for(int i = 0;i <= m;i++)
{
S[i] = 0;
U[i] = D[i] = i;
L[i] = i-1;
R[i] = i+1;
}
R[m] = 0; L[0] = m;
size = m;
for(int i = 1;i <= n;i++)
H[i] = -1;
}
void Link(int r,int c)
{
++S[Col[++size]=c];
Row[size] = r;
D[size] = D[c];
U[D[c]] = size;
U[size] = c;
D[c] = size;
if(H[r] < 0)H[r] = L[size] = R[size] = size;
else
{
R[size] = R[H[r]];
L[R[H[r]]] = size;
L[size] = H[r];
R[H[r]] = size;
}
}
void remove(int c)
{
L[R[c]] = L[c]; R[L[c]] = R[c];
for(int i = D[c];i != c;i = D[i])
for(int j = R[i];j != i;j = R[j])
{
U[D[j]] = U[j];
D[U[j]] = D[j];
--S[Col[j]];
}
}
void resume(int c)
{
for(int i = U[c];i != c;i = U[i])
for(int j = L[i];j != i;j = L[j])
++S[Col[U[D[j]]=D[U[j]]=j]];
L[R[c]] = R[L[c]] = c;
}
//d为递归深度
bool Dance(int d)
{
if(R[0] == 0)
{
ansd = d;
return true;
}
int c = R[0];
for(int i = R[0];i != 0;i = R[i])
if(S[i] < S[c])
c = i;
remove(c);
for(int i = D[c];i != c;i = D[i])
{
ans[d] = Row[i];
for(int j = R[i]; j != i;j = R[j])remove(Col[j]);
if(Dance(d+1))return true;
for(int j = L[i]; j != i;j = L[j])resume(Col[j]);
}
resume(c);
return false;
}
};
重复覆盖
///n row m col, ansd(INF)
using namespace std;
const int MaxM = 15*15+10;
const int MaxN = 15*15+10;
const int maxnode = MaxN * MaxM;
const int INF = 0x3f3f3f3f;
struct DLX
{
int n,m,size;
int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
int H[MaxN],S[MaxM];
int ansd;
void init(int _n,int _m)
{
n = _n;
m = _m;
for(int i = 0;i <= m;i++)
{
S[i] = 0;
U[i] = D[i] = i;
L[i] = i-1;
R[i] = i+1;
}
R[m] = 0; L[0] = m;
size = m;
for(int i = 1;i <= n;i++)H[i] = -1;
}
void Link(int r,int c)
{
++S[Col[++size]=c];
Row[size] = r;
D[size] = D[c];
U[D[c]] = size;
U[size] = c;
D[c] = size;
if(H[r] < 0)H[r] = L[size] = R[size] = size;
else
{
R[size] = R[H[r]];
L[R[H[r]]] = size;
L[size] = H[r];
R[H[r]] = size;
}
}
void remove(int c)
{
for(int i = D[c];i != c;i = D[i])
L[R[i]] = L[i], R[L[i]] = R[i];
}
void resume(int c)
{
for(int i = U[c];i != c;i = U[i])
L[R[i]] = R[L[i]] = i;
}
bool v[MaxM];
int f()
{
int ret = 0;
for(int c = R[0]; c != 0;c = R[c])v[c] = true;
for(int c = R[0]; c != 0;c = R[c])
if(v[c])
{
ret++;
v[c] = false;
for(int i = D[c];i != c;i = D[i])
for(int j = R[i];j != i;j = R[j])
v[Col[j]] = false;
}
return ret;
}
void Dance(int d)
{
if(d + f() >= ansd)return;
if(R[0] == 0)
{
if(d < ansd)ansd = d;
return;
}
int c = R[0];
for(int i = R[0];i != 0;i = R[i])
if(S[i] < S[c])
c = i;
for(int i = D[c];i != c;i = D[i])
{
remove(i);
for(int j = R[i];j != i;j = R[j])remove(j);
Dance(d+1);
for(int j = L[i];j != i;j = L[j])resume(j);
resume(i);
}
}
};
字符串处理
HASH
typedef unsigned long long ULL;
const int SIZE = 100003;
const int SEED = 13331;
const int MAX_N = 50000 + 10;
char s[MAX_N];
struct HASH{
ULL H[MAX_N];
ULL XL[MAX_N];
int len;
HASH(){}
void build(char *s){
len=strlen(s);
H[len]=0;
XL[0]=1;
for (int i=len-1;i>=0;i--){
H[i]=H[i+1]*SEED+s[i];
XL[len-i]=XL[len-i-1]*SEED;
}
}
ULL hash(int i,int L){
return H[i]-H[i+L]*XL[L];
}
}hs;
LCP
int lcp(int i,int j){
int l=0,r=min(len-i,len-j);
int res=0;
while (l<=r){
int mid=(l+r)/2;
if (hash(i,mid)==hash(j,mid)){
res=mid;
l=mid+1;
}
else{
r=mid-1;
}
}
return res;
}
KMP
const int MAXN = 2000010;
struct KMP
{
int f[MAXN];
void getFail(char S[])
{
int i=0,j=-1;
int len=strlen(S);
f[0]=-1;
while (i<len)
{
if (j==-1||S[i]==S[j])
{
i++,j++;
f[i]=j;
}
else
j=f[j];
}
}
///Whether S is a substring of T
///Or whether T has S;
int beat(char T[],char S[])
{
int i=0,j=0;
int n=strlen(T);
int m=strlen(S);
getFail(S);
while(i<n)
{
if(j==-1||T[i]==S[j])
i++,j++;
else
j=f[j];
if(j==m)
return 1;
}
return 0;
}
}soul;
int beat(char T[],char S[])
{
int i=0,j=0;
int n=strlen(T);
int m=strlen(S);
getFail(S);
for(i=0;i<n;i++)
{
while(j!=-1&&T[i]!=S[j])
j=f[j];
j++;
if(j==m)
return 1;
}
return 0;
}
拓展KMP
S串为母串,T串为子串;extend为T与S串的每个后缀的最长前缀长度。
const int MM=100005;
int next[MM],extand[MM];
char S[MM],T[MM];
void GetNext(const char *T){
int len=strlen(T),a=0;
next[0]=len;
while(a<len-1 && T[a]==T[a+1]) a++;
next[1]=a;
a=1;
for(int k=2;k<len;k++){
int p=a+next[a]-1,L=next[k-a];
if( (k-1)+L >= p){
int j = (p-k+1)>0 ? (p-k+1) : 0;
while(k+j<len && T[k+j]==T[j]) j++;
next[k]=j;
a=k;
}
else
next[k]=L;
}
}
void GetExtand(const char *S,const char *T){
GetNext(T);
int slen=strlen(S),tlen=strlen(T),a=0;
int MinLen = slen < tlen ? slen : tlen;
while(a<MinLen && S[a]==T[a]) a++;
extand[0]=a;
a=0;
for(int k=1;k<slen;k++){
int p=a+extand[a]-1, L=next[k-a];
if( (k-1)+L >= p){
int j= (p-k+1) > 0 ? (p-k+1) : 0;
while(k+j<slen && j<tlen && S[k+j]==T[j]) j++;
extand[k]=j;
a=k;
}
else
extand[k]=L;
}
}
int main(){
while(scanf("%s%s",S,T)==2){
GetExtand(S,T);
for(int i=0;i<strlen(T);i++)
printf("%d ",next[i]);
puts("");
for(int i=0;i<strlen(S);i++)
printf("%d ",extand[i]);
puts("");
}
return 0;
}
AC自动机
const int NODE = 1e5+10,CH=26;
int queue[NODE];
struct DFA
{
int ch[NODE][CH],f[NODE],val[NODE],last[NODE],sz;
void init()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
void nn(int u,int c)
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
void ins(char *s)
{
int u=0;
for(;*s;s++)
{
int c=idx(*s);
if(!ch[u][c])
nn(u,c);
u=ch[u][c];
}
val[u]=1;
}
void getfail()
{
int *rear=queue,*front=queue;
for(int c=0;c<CH;c++)
{
int u=ch[0][c];
if(u)
f[u]=0,last[u]=0,*rear++=u;
}
while(rear!=front)
{
int cur = *front++;
for(int c=0;c<CH;c++)
{
int u=ch[cur][c];
int fail=f[cur];
if(u)
{
while(fail && !ch[fail][c]) fail = f[fail];
f[u]=ch[fail][c];
last[u]=val[f[u]]?f[u]:last[f[u]];
*rear++=u;
}
}
}
}
int calc(char *s)
{
int sum=0;
int u=0;
for(;*s;s++)
{
int c=idx(*s);
while(u&&!ch[u][c]) u=f[u];
u=ch[u][c];
for(int tmp=u;tmp;tmp=last[tmp])
sum+=val[tmp];
}
//puts("");
return sum;
}
int search(char *s)
{
int u=0;
for(; *s; s++)
{
int c=idx(*s);
if(!ch[u][c])
return 0;
u=ch[u][c];
}
return val[u];
}
}soul;
后缀数组
const int maxn=3e5*2+10;
/******************************************************************
** 后缀数组 Suffix Array
** INIT:solver.call_fun(char* s);
** CALL: solver.lcp(int i,int j); //后缀i与后缀j的最长公共前缀
** SP_USE: solver.LCS(char *s1,char* s2); //最长公共字串
******************************************************************/
struct SuffixArray{
int r[maxn];
int sa[maxn],rank[maxn],height[maxn];
int t[maxn],t2[maxn],c[maxn],n;
int m;//模板长度
void init(char* s){
n=strlen(s);
for (int i=0;i<n;i++) r[i]=int(s[i]);
m=300;
}
int cmp(int *r,int a,int b,int l){
return r[a]==r[b]&&r[a+l]==r[b+l];
}
/**
字符要先转化为正整数
待排序的字符串放在r[]数组中,从r[0]到r[n-1],长度为n,且最大值小于m。
所有的r[i]都大于0,r[n]无意义算法中置0
函数结束后,结果放在sa[]数组中(名次从1..n),从sa[1]到sa[n]。s[0]无意义
**/
void build_sa(){
int i,k,p,*x=t,*y=t2;
r[n++]=0;
for (i=0;i<m;i++) c[i]=0;
for (i=0;i<n;i++) c[x[i]=r[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 (k=1,p=1;k<n;k*=2,m=p){
for (p=0,i=n-k;i<n;i++) y[p++]=i;
for (i=0;i<n;i++) if (sa[i]>=k) y[p++]=sa[i]-k;
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<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
}
n--;
}
/**
height[2..n]:height[i]保存的是lcp(sa[i],sa[i-1])
rank[0..n-1]:rank[i]保存的是原串中suffix[i]的名次
**/
void getHeight(){
int i,j,k=0;
for (i=1;i<=n;i++) rank[sa[i]]=i;
for (i=0;i<n;i++){
if (k) k--;
j=sa[rank[i]-1];
while (r[i+k]==r[j+k]) k++;
height[rank[i]]=k;
}
}
int d[maxn][20];
//元素从1编号到n
void RMQ_init(int A[],int n){
for (int i=1;i<=n;i++) d[i][0]=A[i];
for (int j=1;(1<<j)<=n;j++)
for (int i=1;i+j-1<=n;i++)
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R){
int k=0;
while ((1<<(k+1))<=R-L+1) k++;
return min(d[L][k],d[R-(1<<k)+1][k]);
}
void LCP_init(){
RMQ_init(height,n);
}
int lcp(int i,int j){
if (rank[i]>rank[j]) swap(i,j);
return RMQ(rank[i]+1,rank[j]);
}
void call_fun(char* s){
init(s);//初始化后缀数组
build_sa();//构造后缀数组sa
getHeight();//计算height与rank
LCP_init();//初始化RMQ
}
int LCS(char* s1,char* s2){
int p,ans;
int l=strlen(s1);
p=l;
s1[l]='$';
s1[l+1]='\0';
strcat(s1,s2);
call_fun(s1);
ans=0;
for (int i=2;i<=n;i++)
if ((sa[i-1]<p&&sa[i]>p)||(sa[i-1]>p&&sa[i]<p)) ans=max(ans,height[i]);
return ans;
}
}solver;
后缀自动机
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#define sz(x) int(x.size())
using namespace std;
typedef vector<int> VI;
const int maxn = 250000+10;
class SuffixAutomaton{
private:
struct Node{
Node *suf, *go[26];
int val;
Node(){
suf=NULL;
val=0;
memset(go,0,sizeof(go));
}
void clear(){
suf=NULL;
val=0;
memset(go,0,sizeof(go));
}
int calc(){
if (suf==0) return 0;
return val-suf->val;
}
};
Node *root,*last;
Node nodePool[maxn*2],*cur;
Node* newNode(){
Node* res=cur++;
res->clear();
return res;
}
int tot;
void extend(int w){
Node *p=last;
Node *np=newNode();
np->val=p->val+1;
while (p&&!p->go[w]){
p->go[w]=np;
p=p->suf;
}
if (!p){
np->suf=root;
tot+=np->calc();
}
else{
Node *q=p->go[w];
if (p->val+1==q->val){
np->suf=q;
tot+=np->calc();
}
else{
Node *nq=newNode();
memcpy(nq->go,q->go,sizeof(q->go));
tot-=p->calc()+q->calc();
nq->val=p->val+1;
nq->suf=q->suf;
q->suf=nq;
np->suf=nq;
tot+=p->calc()+q->calc()+np->calc()+nq->calc();
while (p&&p->go[w]==q){
p->go[w]=nq;
p=p->suf;
}
}
}
last = np;
}
public:
void init(){
cur=nodePool;
root=newNode();
last=root;
}
VI getSubString(char s[]){
VI v;
tot=0;
int len=strlen(s);
for (int i=0;i<len;i++){
extend(s[i]-'a');
v.push_back(tot);
}
return v;
}
int getLCS(char A[],char B[]){
int res=0,step=0;
int lenA=strlen(A);
int lenB=strlen(B);
for (int i=0;i<lenA;i++) extend(A[i]-'a');
Node *p=root;
for (int i=0;i<lenB;i++){
int x=B[i]-'a';
if (p->go[x]){
step++;
p=p->go[x];
}
else{
while (p&&!p->go[x]) p=p->suf;
if (!p){
p=root;
step=0;
}
else{
step=p->val+1;
p=p->go[x];
}
}
res=max(res,step);
}
return res;
}
}atm;
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxn=300000;
const int maxm=160000;
/***************
SAM 真·模板
***************/
struct State {
State *par;
State *go[52];
int val; // max,当前状态能接收的串的最长长度
int mi; // min,当前状态能接受的串的最短长度,即 par->val+1
int cnt; // 附加域,用来计数
int right; // right集,表示当前状态可以在多少个位置上出现
void init(int _val = 0){
par = 0;
val = _val;
cnt=0;
mi=0;
right=0;
memset(go,0,sizeof(go));
}
int calc(){ // 表示该状态能表示多少中不同的串
if (par==0) return 0;
return val-par->val;
}
};
State *root, *last, *cur;
State nodePool[maxn];
State* newState(int val = 0) {
cur->init(val);
return cur++;
}
//int total; // 不同的子串个数。
void initSAM() {
//total = 0;
cur = nodePool;
root = newState();
last = root;
}
void extend(int w) {
State* p = last;
State* np = newState(p->val + 1);
np->right=1; // 设置right集
while (p && p->go[w] == 0) {
p->go[w] = np;
p = p->par;
}
if (p == 0) {
np->par = root;
//total+=np->calc();
}
else {
State* q = p->go[w];
if (p->val + 1 == q->val) {
np->par = q;
//total+=np->calc();
}
else {
State* nq = newState(p->val + 1);
memcpy(nq->go, q->go, sizeof(q->go));
//total -= q->calc();
nq->par = q->par;
q->par = nq;
np->par = nq;
//total += q->calc()+nq->calc()+np->calc();
while (p && p->go[w] == q) {
p->go[w] = nq;
p = p->par;
}
}
}
last = np;
}
int d[maxm];
State* b[maxn];
void topo(){ // 求出parent树的拓扑序
int cnt=cur-nodePool;
int maxVal=0;
memset(d,0,sizeof(d));
for (int i=1;i<cnt;i++) maxVal=max(maxVal,nodePool[i].val),d[nodePool[i].val]++;
for (int i=1;i<=maxVal;i++) d[i]+=d[i-1];
for (int i=1;i<cnt;i++) b[d[nodePool[i].val]--]=&nodePool[i];
b[0]=root;
}
void gaoSamInit(){ // 求出SAM的附加信息
State* p;
int cnt=cur-nodePool;
for (int i=cnt-1;i>0;i--){
p=b[i];
p->par->right+=p->right;
p->mi=p->par->val+1;
}
}
char s[maxm];
const int INF=0x3f3f3f3f;
int gao(char s[]){
int ans=INF;
int cnt=cur-nodePool;
int len=strlen(s);
int lcs=0;
State* p=root;
for (int i=0;i<len;i++){
int son=s[i]-'a';
if (p->go[son]!=0){
lcs++;
p=p->go[son];
}
else{
while (p&&p->go[son]==0) p=p->par;
if (p==0){
lcs=0;
p=root;
}
else{
lcs=p->val+1;
p=p->go[son];
}
}
// TODO:
if (lcs>0) p->cnt++;
}
for (int i=cnt-1;i>0;i--){
p=b[i];
// TODO:
if (p->right==1&&p->cnt==1) ans=min(ans,p->mi);
p->par->cnt += p->cnt;
}
return ans;
}
后缀自动机的经典问题
uva 719 - Glass Beads 最小循环串
后缀自动机的遍历。
给一个字符串S,每次可以将它的第一个字符移到最后面,求这样能得到的字典序最小的字符串。将字符串S拼接为SS,构造自动机,从根结点开始每次走最小编号的边,移动length(S)步就可以找到字典序最小的串。
由于 SAM 可以接受 SS 所有的子串,而字典序最小的字符串也必定是 SS 的子串,因此按照上面的规则移动就可以找到一个字典序最小的子串。
spoj 1811 Longest Common Substring 最长公共子串
给两个长度小于100000的字符串 A 和 B,求出他们的最长公共连续子串。先将串 A 构造为 SAM ,然后用 B 按如下规则去跑自动机。
用一个变量 lcs 记录当前的最长公共子串,初始化为0。
设当前状态结点为 p,要匹配的字符为 c,若 go[c] 中有边,说明能够转移状态,则转移并 lcs++;
若不能转移则将状态移动到 p 的 par ,如果仍然不能转移则重复该过程直到 p 回到根节点,并将 lcs 置为 0;
如果在上一个过程中进入了能够转移的状态,则设 lcs 为当前状态的 val。
为什么失配后要移向 par 呢?因为在状态 p 上失配说明该状态的 [min,max] 所表示字符串都不是 B 中的子串,但是比它们短的后缀仍有可能是 B 的子串,而 par 指针恰好指向了该状态的后缀。
spoj 1812 Longest Common Substring II 多个串的最长公共子串
在上一题中我们知道了如何求两个串的最长公共子串,本题则是要求多个串的最长公共子串。本题要用到 parent 树的拓扑序。
首先用第一个串构造 SAM,然后用其他的串匹配它。
SAM 的状态要多维护两个信息:lcs,当多个串的最长公共子串的最后一个字符落在该状态上的长度;nlcs,当前串的最长公共子串的最后一个字符落在该状态上的长度。
我们对每个串的匹配之后,要对每个状态的 lcs 进行维护,显然 lcs=min(lcs, nlcs),而我们最后所求的就是所有状态中 lcs 的最大值。
匹配的过程与上一题相同,但是在匹配过程中,到达状态 p 时得到的 nlcs 未必就是该状态能表示的最长公共子串长,因为如果一个子串出现了n次,那么子串的所有后缀也至少出现了n次。
因此在每个串匹配之后求要按照拓扑序的逆序维护每个状态的 nlcs,使 p->par->nlcs=max(p->nlcs, p->par->nlcs)。
hdu 4622 Reincarnation 统计不同子串个数
这也是许多新人第一次接触到 SAM 的题。本题可以用各种姿势 AC,但是用 SAM 最轻松。给出一个字符串,最长2000,q个询问,每次询问[l,r]区间内有多少个不同的字串。
SAM 中的每个状态能够表示的不同子串的个数为 val - 父结点的 val。因此在构造自动机时,用变量 total 记录当前自动机能够表示的不同子串数,对每一次 extend 都更新 total 的值。将这个过程中的每一个 total 值都记录下了就能得到一个表示子串个数表。我们对字符串的每一个后缀都重新构造一遍 SAM 就可以得到一个二维的表。
对每次询问,在表中查找相应的值即可。
hdu 4436 str2int 处理不同的子串
给出n个数字,数字很长,用字符串读入,长度总和为10^5。求这n个字符串的所有子串(不重复)的和取模2012 。题目要对所有不重复的子串进行处理,考虑使用 SAM 来将解决。
将 n 个数字拼接成一个字符串,用不会出现的数字 10 进行分割。
构造完之后按照拓扑序计算每个状态上的 sum 与 cnt,sum 表示以当前状态为结尾的子串的和,cnt 表示有多少种方法到达当前结点。
设父结点为 u 向数字 k 移动到的子结点为 v, 显然结点 v 的状态要在 sum 上增加 add=u->sum*10+u->cnt*k。即 u 的能表示的数字总和乘上10再加上到达 v 的方法总数乘上当前的个位数字 k。
最后答案就是将所有状态的 sum 求和。
spoj 8222 Substrings 子串出现次数
给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。求F(1)..F(Length(S)) 。在拓扑序的逆序上维护每个状态的 right,表示当前状态的出现次数。
最后当前用每个状态的 right 来更新 f[val],即当前状态能表示的最长串的出现次数。
最后用 f[i] 依次去更新 f[i-1] 取最大值,因为若一个长度为 i 的串出现了 f[i] 次,那么长度为 i-1 的串至少出现 f[i] 次。
poj 3415Common Substrings 子串计数
给出两个串,问这两个串的所有的子串中(重复出现的,只要是位置不同就算两个子串),长度大于等于k的公共子串有多少个。先对第一个串构造 SAM,通过状态的 right 与 val 可以轻松求出它能表示的所有子串数。现在的问题是如何满足条件。
用第二个串对 SAM 做 LCS,当前状态 LCS >= K 时,维护状态上的 cnt++,表示该状态为大于K且最长公共串的结尾的次数为 cnt 次。
统计最长公共子串的状态中满足条件的个数 ans+=(lcs-max(K,p->mi)+1)*p->right
匹配结束后,用拓扑序的逆序维护每个状态父结点 cnt,此时 cnt 的含义为该状态被包含的次数。
统计不是最长公共子串的状态但是被子串包含的个数,ans+=p->cnt*(p->par->val - max(K,p->par->mi)+1)*p->par->right,用父结点被包含的次数乘以满足条件的串数累加到答案中。
spoj 7258 Lexicographical Substring Search 求字典序
给出一个字符串,长度为90000。询问q次,每次回答一个k,求字典序第k小的子串。仍然用拓扑序得到每个状态拥有的不同子串数。
对第k小的子串,按字典序枚举边,跳过一条边则 k 减去该边指向的状态的不同子串数,直到不能跳过,然后沿着该边移动一次,循环这个步骤直到 k变为0。
此时的路径就是字典序第k小的子串。
Codeforces 235C Cyclical Quest 串的出现次数
*这场比赛的出题人是 WJMZBMR 陈立杰*给出一个字符串s,这里称之为母串,然后再给出n个子串,n<=10^5,子串长度总和不超过10^6。问,对于每一个子串的所有不同的周期性的同构串在母串中出现的次数总和。
将母串构造 SAM,将子串复制拼接到一起然后去掉最后一个字母去跑 SAM。
对满足条件的状态向上维护直到原子串的长度包含在了状态能表示的长度中并用 mark 标记。
然后将该状态的出现次数累加到答案上,如果一个应该累加的状态已经被 mark 过了,就不再累加。
Codeforces 427D Match & Catch 公共串的出现次数
给出两个长度均不超过5000的字符串s1,s2,求这两个串中,都只出现一次的最短公共子串。对第一个串构造 SAM,用第二个串跑。显然 right 为1的状态就是在第一个串中出现次数为1的子串。
匹配过程总的每进入一个结点,就将结点上的 cnt 加一,表示该状态表示的最长公共串在第二个串的出现次数。
最后按拓扑序逆序求出所有状态的 cnt,若一个结点出现过 cnt 次,那么他的父结点即它的后缀出现次数也要加上 cnt。
最后遍历所有的状态,right 等于 1 且 cnt 等于 1 的状态就是出现次数为1的公共子串,找到其中最短的作为答案即可。
回文自动机
const int MAXN = 100005 ;
const int N = 26 ;
struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
cnt[p] = 0 ;
num[p] = 0 ;
len[p] = l ;
return p ++ ;
}
void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
return x ;
}
void add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + 1 ;
}
last = next[cur][c] ;
cnt[last] ++ ;
}
void count () {
for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
} ;
manacher
int Proc(char pszIn[],char pszOut[])
{
int nLen=1;
pszOut[0]='$';
int i=0;
while(pszIn[i]!='\0')
{
pszOut[nLen++]='#';
pszOut[nLen++]=pszIn[i];
i++;
}
pszOut[nLen++]='#';
pszOut[nLen]=0;
return nLen;
}
void Manacher(int *p,char *str,int len)
{
int mx=0,id=0;
for(int i=0;i<len;i++)
{
p[i]=mx>i?min(p[2*id-i],mx-i):1;
while(str[i+p[i]]==str[i-p[i]])p[i]++;
if(i+p[i]>mx)
{
mx=i+p[i];
id=i;
}
}
}
const int MAXN=220010;
char strIn[MAXN];
char strOut[MAXN];
int p[MAXN];
int main()
{
while(scanf("%s",strIn)!=EOF)
{
int nLen=Proc(strIn,strOut);
Manacher(p,strOut,nLen);
int ans=1;
for(int i=0;i<nLen;i++)
ans=max(ans,p[i]);
printf("%d\n",ans-1);
}
return 0;
}
struct Manacher
{
char str[MAXN];
int p[MAXN];
int n;
void getP()
{
int mx=0,id=0;
for(int i=0;i<n;i++)
{
p[i]=mx>i?min(p[2*id-i],mx-i):1;
while(str[i+p[i]]==str[i-p[i]])
p[i]++;
if(i+p[i]>mx)
{
mx=i+p[i];
id=i;
}
}
}
int call(char *s)
{
n=0;
str[n++]='$';
for(;*s;s++)
{
str[n++]='#';
str[n++]=*s;
}
str[n++]='#';
str[n]=0;
getP();
int ret=1;
for(int i=0;i<n;i++)
ret=max(ret,p[i]);
return ret-1;
}
}soul;
最大最小表示法
int min_max_reprecention(int flag) //最小最大表示法0、1
{
int i=0,j=1,k=0;
while(i<wlen&&j<wlen&&k<wlen)
{
int t=word[(i+k)%wlen]-word[(j+k)%wlen];
if(!t) k++;
else
{
if(flag==0)
{
if(t>0) i=i+k+1;
else j=j+k+1;
}
else
{
if(t>0) j=j+k+1;
else i=i+k+1;
}
if(i==j) j++;
k=0;
}
}
return i<j?i:j;
} //返回是从0开始,如果问第几个需加1
数据结构
pb_ds库
///这些封装好的树都支持插入(insert)、删除(erase)、求kth(find_by_order)、求rank(order_of_key)操作
#include<cstdio>
#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
int main() {
/**
* spoj3273, G++4.3.2, 4.66s
* tree<int,null_mapped_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> bbt;
*/
/**
* spoj3273, G++4.3.2, TLE
* tree<int,null_mapped_type,less<int>,splay_tree_tag,tree_order_statistics_node_update> bbt;
*/
/**
* spoj3273, G++4.3.2, TLE
* tree<int,null_mapped_type,less<int>,ov_tree_tag,tree_order_statistics_node_update> bbt;
*/
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> bbt;
int n,num;
char c;
scanf("%d",&n);
while(n--) {
scanf(" %c%d",&c,&num);
switch(c) {
case 'I':
bbt.insert(num);
break;
case 'D':
bbt.erase(num);
break;
case 'K':
num<=bbt.size()?printf("%d\n",*bbt.find_by_order(num-1)):puts("invalid");
break;
case 'C':
printf("%d\n",bbt.order_of_key(num));
break;
}
}
}
/*
此外,我手写了Treap和Size Balanced Tree(代码在此)并经过测试,前者3.94s,后者4.06s,
可见手写数据结构的速度总归要比封装好的要快,而封装好的实现中红黑树速度最快,这也是意料之中的。
关于声明变量时的参数,第一个是键(key)的类型;第二个是值(value)的类型,null_type表示没有值,
简单地理解就是表明这是set而不是map,注意SPOJ的G++版本稍旧(4.3.2),需要写成null_mapped_type才可以,我本地的G++版本为4.7.1;
第三个表示比较函数,默认为less<Type>;第四个为平衡二叉树的类型,默认为红黑树rb_tree_tag;第五个代表元素的维护策略,
只有当使用tree_order_statistics_node_update时才可以求kth和rank,此外还有null_tree_node_update(默认值)等,具体的区别在官方文档中有介绍(好吧我承认我没看懂= =)。
这里要注意的是,求kth(find_by_order)返回的是迭代器,求rank返回的是值,两者都是从0开始计算的。
此外,它们也支持合并(join)和分离(split)操作。用法如下。
tree<int,null_type> a,b;
// 对两平衡二叉树进行一些操作
a.join(b);
// 对两平衡二叉树进行一些操作
a.split(v,b);
*/
树状数组
(1)“改点求段”型,即对于序列A有以下操作:
【1】修改操作:将A[x]的值加上c;
【2】求和操作:求此时A[l..r]的和。
void ADD(int x, int c)
{
for (int i=x; i<=n; i+=i&(-i)) a[i] += c;
}
int SUM(int x)
{
int s = 0;
for (int i=x; i>0; i-=i&(-i)) s += a[i];
return s;
}
操作【1】:ADD(x, c);
操作【2】:SUM(r)-SUM(l-1)。
(2)“改段求点”型,即对于序列A有以下操作:
【1】修改操作:将A[l..r]之间的全部元素值加上c;
【2】求和操作:求此时A[x]的值。
void ADD(int x, int c)
{
for (int i=x; i>0; i-=i&(-i)) b[i] += c;
}
int SUM(int x)
{
int s = 0;
for (int i=x; i<=n; i+=i&(-i)) s += b[i];
return s;
}
操作【1】:ADD(l-1, -c); ADD(r, c);
操作【2】:SUM(x)。
【1】修改操作:将A[l..r]之间的全部元素值加上c;
【2】求和操作:求此时A[l..r]的和。
操作【2】:SUM(x)。
(3)“改段求段”型,即对于序列A有以下操作:
【1】修改操作:将A[l..r]之间的全部元素值加上c;
【2】求和操作:求此时A[l..r]的和。
void ADD_B(int x, int c)
{
for (int i=x; i>0; i-=i&(-i)) B[i] += c;
}
void ADD_C(int x, int c)
{
for (int i=x; i<=n; i+=i&(-i)) C[i] += x * c;
}
int SUM_B(int x)
{
int s = 0;
for (int i=x; i<=n; i+=i&(-i)) s += B[i];
return s;
}
int SUM_C(int x)
{
int s = 0;
for (int i=x; i>0; i-=i&(-i)) s += C[i];
return s;
}
inline int SUM(int x)
{
if (x) return SUM_B(x) * x + SUM_C(x - 1); else return 0;
}
操作【1】:
ADD_B(r, c); ADD_C(r, c);
if (l > 1) {ADD_B(l - 1, -c); ADD_C(l - 1, -c);}
操作【2】:SUM(r) - SUM(l - 1)。
ADD_B(r, c); ADD_C(r, c);
if (l > 1) {ADD_B(l - 1, -c); ADD_C(l - 1, -c);}
操作【2】:SUM(r) - SUM(l - 1)。
树链剖分
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e4+100;
struct Graph
{
SegTree tr;
struct Edge
{
int to;
int w;
int next;
} edges[MAXN<<1];
int head[MAXN],edge;
void init()
{
memset(head,-1,sizeof(head));
cur=edge=0;
wei[1]=0;
}
void addEdge(int u,int v,int w)
{
edges[edge].w=w,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;
edges[edge].w=w,edges[edge].to=u,edges[edge].next=head[v],head[v]=edge++;
}
int fa[MAXN],son[MAXN],dep[MAXN],wei[MAXN],num[MAXN];
void build(int u=1,int f=0,int d=0)
{
dep[u] = d;
fa[u] = f;
son[u]=-1;
num[u]=1;
for(int i = head[u]; i != -1; i = edges[i].next)
{
int v = edges[i].to;
if(v != f)
{
build(v,u,d+1);
wei[v]=edges[i].w;
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])
son[u] = v;
}
}
}
int seg[MAXN],top[MAXN],cur;
void split(int u=1,int tp=1)
{
seg[u]=++cur;
top[u]=tp;
if(son[u]!=-1)
split(son[u],tp);
for(int i=head[u]; i!=-1; i=edges[i].next)
{
int v=edges[i].to;
if(v==fa[u]||v==son[u])
continue;
split(v,v);
}
}
void call()
{
init();
int a,b,c;
for(int i=1; i<n; i++)
scanf("%d%d%d",&a,&b,&c),addEdge(a,b,c);
build();
split();
for(int i=1; i<=n; i++)
tr.update(seg[i],wei[i]);
}
} soul;
const int maxn=101010+5;
const int maxm=maxn+maxn;
struct EDGENODE{
int to;
int w;
int next;
}edges[maxm];
int head[maxn],edge;
inline void init(){
edge=0;
memset(head,-1,sizeof(head));
}
inline void addedge(int u,int v,int w){
edges[edge].w=w,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;
edges[edge].w=w,edges[edge].to=u,edges[edge].next=head[v],head[v]=edge++;
}
int que[maxn]; // 队列
bool vis[maxn]; // 访问标记
int son[maxn]; // 重儿子
int idx[maxn]; // 结点v在其路径中的编号
int dep[maxn]; // 结点v的深度
int siz[maxn]; // 以结点v为根的子树的结点个数
int belong[maxn]; // 结点v所属的路径编号
int fa[maxn]; // 结点v的父亲结点
int top[maxn]; // 编号为p的路径的顶端结点
int len[maxn]; // 路径p的长度
int sump[maxn]; // 路径p的编号
int seg[maxn]; // 结点v的父边在线段树中的位置
int wei[maxn]; // 结点v的父边的权值
int l,r,ans,cnt;
int n;
char cmd[22];
void split(){
memset(dep,-1,sizeof(dep));
l=0;
dep[ que[r=1]=1 ]=0; // 将根结点插入队列,并设深度为0
fa[1]=-1; // 默认 1 为根结点
wei[1]=0;
while (l<r){ // 第一遍搜索求出 fa,dep,wei
int u=que[++l];
vis[u]=false; // 顺便初始化vis
for (int i=head[u];i!=-1;i=edges[i].next){
int v=edges[i].to;
int w=edges[i].w;
if (dep[v]==-1){ // 未访问过的结点
dep[ que[++r]=v ]=dep[u]+1; // 将v插入队列并设深度为dep[u]+1
fa[v]=u; // v的父结点为u
wei[v]=w; // v的父边权值
}
}
}
cnt=0; // 重链编号
for (int i=n;i>0;i--){
int u=que[i],p=-1;
siz[u]=1;
son[u]=p;
for (int k=head[u];k!=-1;k=edges[k].next){
int v=edges[k].to;
if (vis[v]){ // 若v是u的子结点
siz[u]+=siz[v]; // 计数
if (p==-1||siz[v]>siz[p]){
son[u]=v;
p=v; // u的重儿子是v
}
}
}
if (p==-1){ // u是叶子结点
idx[u]=len[++cnt]=1; // 一个新的路径编号为cnt,u是路径中的第一个结点
belong[ top[cnt]=u ]=cnt; // u是顶端结点,且u属于路径cnt
}
else{ // u不是叶子结点
idx[u]=++len[ belong[u]=belong[p] ]; // u属于重儿子所在的链,链长+1,u是路径中第len个结点
top[ belong[u] ]=u; // u是顶端结点
}
vis[u]=true; // 访问标记
}
}
int find(int va,int vb){
int f1=top[belong[va]],f2=top[belong[vb]],tmp=-INF;
while (f1!=f2){
if (dep[f1]<dep[f2]){
swap(f1,f2);
swap(va,vb);
}
tmp=max(tmp,tr.query(1,seg[f1],seg[va]));
va=fa[f1];
f1=top[belong[va]];
}
if (va==vb) return tmp;
if (dep[va]>dep[vb]) swap(va,vb);
return max(tmp,tr.query(1,seg[son[va]],seg[vb]));
}
sump[0]=0;
for (int i=1;i<=cnt;i++) sump[i]=sump[i-1]+len[i];
for (int i=1;i<=n;i++){
seg[i]=sump[ belong[i] ]-idx[i]+1;
tr.num[ seg[i] ]=wei[i];
}
Treap
const int MAXN = 1e5+10;
struct Node
{
Node*ch[2];
int r;
int v;
int son;
void init(int _v)
{
v=_v;
ch[0]=ch[1]=0;
r=rand();
son=1;
}
int cmp(int x) const
{
if(x==v) return -1;
return x<v?0:1;
}
void maintain()
{
son=1;
if(ch[0]) son+=ch[0]->son;
if(ch[1]) son+=ch[1]->son;
}
}nodePool[MAXN],*cur,*rt;
struct Treap
{
Node* newNode(int v=0)
{
cur->init(v);
return cur++;
}
void init()
{
cur=nodePool;
rt=NULL;
}
void rotate(Node *o,int d)
{
Node *k= o->ch[d^1];
o->ch[d^1] = k->ch[d];
k->ch[d]=o;
o->maintain();
k->maintain();
o=k;
}
void insert(Node* &o,int x)
{
if(o==NULL) o = newNode(x);
else
{
///multiset
int d=(x<o->v?0:1);
///set use cmp() and check -1;
insert(o->ch[d],x);
if( o->ch[d]->r> o->r) rotate(o,d^1);
}
o->maintain();
}
void remove(Node*&o,int x)
{
int d=o->cmp(x);
if(d==-1)
{
Node*u=o;
if(o->ch[0]!=NULL&&o->ch[1]!=NULL)
{
int d2=o->ch[0]->r>o->ch[1]->r?1:0;
rotate(o,d2);
remove(o->ch[d2],x);
}
else
{
if(o->ch[0]==NULL)
o=o->ch[1];
else
o=o->ch[0];
}
}
else
{
remove(o->ch[d],x);
if(o!=NULL)
o->maintain();
}
}
int kth(Node *o,int k)
{
if(o==NULL||k<=0||k>o->son)
return 0;
int s=(o->ch[1]==NULL?0:o->ch[1]->son);
if(k==s+1)
return o->v;
else if(k<=s)
return kth(o->ch[1],k);
else
return kth(o->ch[0],k-s-1);
}
void mergeto(Node *&src,Node *&dest)
{
if(src->ch[0]!=NULL)
mergeto(src->ch[0],dest);
if(src->ch[1]!=NULL)
mergeto(src->ch[1],dest);
insert(dest,src->v);
src=NULL;
}
};
Splay
const int MAXN = 1e5+10;
struct Node
{
Node*ch[2];
int v;
int son;
/***FLIP***\
大白P243
operation to call:
Node *left,*mid,*right,*o;
split(root,a,left,0);
split(o,b-a+1,mid,right);
mid->flip^=1;
root=merge(merge(left,right),mid);
ATTENTION!
need a virtual point at BEGIN
\***FLIP***/
int flip;
void pushdown()
{
if(flip)
{
flip=0;
swap(ch[0],ch[1]);
ch[0]->flip=!ch[0]->flip;
ch[1]->flip=!ch[1]->flip;
}
}
void init(int _v);
int cmp(int x) const
{
if(x==v) return -1;
return x<v?0:1;
}
void maintain()
{
son=1;
if(ch[0]) son+=ch[0]->son;
if(ch[1]) son+=ch[1]->son;
}
}nodePool[MAXN],*cur,*rt,*null;
void Node::init(int v)
{
v=_v;
ch[0]=ch[1]=null;
r=rand();
son=1;
flip=0;
}
void initNULL()
{
null=new Node();
null->init(0);
null->son=0;
}
struct Treap
{
Node* newNode(int v=0)
{
cur->init(v);
return cur++;
}
void init()
{
cur=nodePool;
rt=NULL;
}
void rotate(Node *o,int d)
{
Node *k= o->ch[d^1];
o->ch[d^1] = k->ch[d];
k->ch[d]=o;
o->maintain();
k->maintain();
o=k;
}
void splay(Node *&o,int k)
{
int d=o->cmp(k);
if(d==1)
k-=o->ch[0]->son+1;
if(d!=-1)
{
Node *p=o->ch[d];
int d2=p->cmp(k);
int k2=(d2==0?k:k-p->ch[0]->son-1);
if(d2!=-1)
{
splay(p->ch[d2],k2);
if(d==d2)
rotate(o,d^1);
else
rotate(o->ch[d],d);
}
rotate(o,d^1);
}
}
///right NOT NULL
Node *merge(Node*left,Node*right)
{
splay(left,left->s);
left->ch[1]=right;
left->maintain();
return left;
}
///ATTENTION:
///前K小在left
///k=[1,o->son];
void split(Node*o,int k,Node*&left,Node*&right)
{
splay(o,k);
left=o;
right=o->ch[1];
o->ch[1]=null;
left->maintain();
}
///
};
树X树
树状数组X线段树(动态区间第K大)
/* ***********************************************
Author :kuangbin
Created Time :2013-9-8 8:53:54
File Name :F:\2013ACM练习\专题学习\主席树\ZOJ2112.cpp
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
const int MAXN = 60010;
const int M = 2500010;
int n,q,m,tot;
int a[MAXN], t[MAXN];
int T[MAXN], lson[M], rson[M],c[M];
int S[MAXN];
struct Query
{
int kind;
int l,r,k;
}query[10010];
void Init_hash(int k)
{
sort(t,t+k);
m = unique(t,t+k) - t;
}
int hash(int x)
{
return lower_bound(t,t+m,x)-t;
}
int build(int l,int r)
{
int root = tot++;
c[root] = 0;
if(l != r)
{
int mid = (l+r)/2;
lson[root] = build(l,mid);
rson[root] = build(mid+1,r);
}
return root;
}
int Insert(int root,int pos,int val)
{
int newroot = tot++, tmp = newroot;
int l = 0, r = m-1;
c[newroot] = c[root] + val;
while(l < r)
{
int mid = (l+r)>>1;
if(pos <= mid)
{
lson[newroot] = tot++; rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root];
r = mid;
}
else
{
rson[newroot] = tot++; lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root];
l = mid+1;
}
c[newroot] = c[root] + val;
}
return tmp;
}
int lowbit(int x)
{
return x&(-x);
}
int use[MAXN];
void add(int x,int pos,int val)
{
while(x <= n)
{
S[x] = Insert(S[x],pos,val);
x += lowbit(x);
}
}
int sum(int x)
{
int ret = 0;
while(x > 0)
{
ret += c[lson[use[x]]];
x -= lowbit(x);
}
return ret;
}
int Query(int left,int right,int k)
{
int left_root = T[left-1];
int right_root = T[right];
int l = 0, r = m-1;
for(int i = left-1;i;i -= lowbit(i)) use[i] = S[i];
for(int i = right;i ;i -= lowbit(i)) use[i] = S[i];
while(l < r)
{
int mid = (l+r)/2;
int tmp = sum(right) - sum(left-1) + c[lson[right_root]] - c[lson[left_root]];
if(tmp >= k)
{
r = mid;
for(int i = left-1; i ;i -= lowbit(i))
use[i] = lson[use[i]];
for(int i = right; i; i -= lowbit(i))
use[i] = lson[use[i]];
left_root = lson[left_root];
right_root = lson[right_root];
}
else
{
l = mid+1;
k -= tmp;
for(int i = left-1; i;i -= lowbit(i))
use[i] = rson[use[i]];
for(int i = right;i ;i -= lowbit(i))
use[i] = rson[use[i]];
left_root = rson[left_root];
right_root = rson[right_root];
}
}
return l;
}
void Modify(int x,int p,int d)
{
while(x <= n)
{
S[x] = Insert(S[x],p,d);
x += lowbit(x);
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int Tcase;
scanf("%d",&Tcase);
while(Tcase--)
{
scanf("%d%d",&n,&q);
tot = 0;
m = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
t[m++] = a[i];
}
char op[10];
for(int i = 0;i < q;i++)
{
scanf("%s",op);
if(op[0] == 'Q')
{
query[i].kind = 0;
scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);
}
else
{
query[i].kind = 1;
scanf("%d%d",&query[i].l,&query[i].r);
t[m++] = query[i].r;
}
}
Init_hash(m);
T[0] = build(0,m-1);
for(int i = 1;i <= n;i++)
T[i] = Insert(T[i-1],hash(a[i]),1);
for(int i = 1;i <= n;i++)
S[i] = T[0];
for(int i = 0;i < q;i++)
{
if(query[i].kind == 0)
printf("%d\n",t[Query(query[i].l,query[i].r,query[i].k)]);
else
{
Modify(query[i].l,hash(a[query[i].l]),-1);
Modify(query[i].l,hash(query[i].r),1);
a[query[i].l] = query[i].r;
}
}
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
pb_ds转自
http://blog.youkuaiyun.com/wjf_wzzc
其他部分来自
http://blog.youkuaiyun.com/cyendra
树状数组来自
http://blog.youkuaiyun.com/q573290534
DLX来自
http://blog.youkuaiyun.com/wjf_wzzc