【BZOJ 4567】[Scoi2016] 背单词

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4567

这个题,就AC自动机搞一搞就可以啦!
省选时的我都能A,这是真简单啊!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define AC AC_Automata
#define LL long long
using namespace std;

LL vout = 0;
char pat[510000+9];

namespace AC_Automata{
	const int N = 510000+9;
	const int SIGMA_SIZE = 26;
	struct Node{int ch[SIGMA_SIZE],fail,last,vis;}p[N];
	int cnt,que[N],itr1,itr2;
	vector<int> G[N];
	
	int head[N],to[N],nxt[N];
	int TOT,tot,sz[N];
	
	inline void AddEdge(int u, int v){to[++TOT] = v; nxt[TOT] = head[u]; head[u] = TOT;}
	
	inline void insert(char *s){
		int len = strlen(s), w=0;
		for (int i=0;i<len;i++){
			int c = s[i] - 'a';
			if (!p[w].ch) w = p[w].ch = ++cnt;
			else w = p[w].ch;
		} p[w].vis++;
	}
	
	inline void GetFail(){
		itr1 = 0; itr2 = 1;
		for (int i=0;i<SIGMA_SIZE;i++)
			if (p[0].ch[i]) que[++itr1] = p[0].ch[i];
		
		while (itr2 <= itr1){
			int w = que[itr2++];
			if (p[w].vis) AddEdge(p[w].last, w);
			for (int i=0;i<SIGMA_SIZE;i++){
				if (p[w].ch[i]) {
					int nw = p[w].fail;
					while (nw && !p[nw].ch[i]) nw = p[nw].fail;
					p[p[w].ch[i]].fail = p[nw].ch[i];
					p[p[w].ch[i]].last = p[p[nw].ch[i]].vis ? p[nw].ch[i] : p[p[nw].ch[i]].last;
					que[++itr1] = p[w].ch[i];
				}
			} 
		}
	}
	
	void DFS(int w){
		sz[w] = 1;
		for (int i=head[w];i;i=nxt[i]) DFS(to[i]), sz[w] += sz[to[i]], G[w].push_back(sz[to[i]]);
		int len = G[w].size();
		sort(G[w].begin(), G[w].end());
		for (int i=0,sta=0;i<len;i++) vout += (LL)sta + 1LL, sta += (LL)G[w][i];
	}
};

int main(){
	int n; scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%s",pat), AC::insert(pat);
	AC::GetFail(); AC::DFS(0);
	printf("%lld",vout);
	return 0;
}

【BZOJ 4523】[Cqoi2016] 路由表

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4523
数据生成器:http://paste.ubuntu.com/23140079/

这个题,Trie树的做法很显然,可以持久,也可以不持久
但此题卡常QAQ
在BZOJ上过的不算,是男人就上Super OJ去交
我被逼着优化成了BZOJ上的Rank11才在SOJ上刚好卡过 (╯‵□′)╯︵┻━┻

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 1000000+9;
const int M = 33000000;

int Stack[N];
bool ip[40];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

namespace Trie{
	#define TRE Trie
	int tot=1,ch[M][2],cnt,root=1,end[M];
	
	inline void modify(int len){
		int w = root; 
		for (int i=1;i<=len;i++) {
			int c = ip[i];
			if (ch[w]) w = ch[w];
			else w = ch[w] = ++tot;
		}
		end[w] = ++cnt;
	}
	
	inline int query(int l, int r){
		int top = 0, w = root, t = 1;
		while (w) {
			if (end[w]) {
				while (top && Stack[top] > end[w]) top--;
				Stack[++top] = end[w];
			}	
			w = ch[w][ip[t]]; t++;
		} t = 0;
		for (int i=1;i<=top;i++) 
			if (Stack[i] > r) break;
			else if (Stack[i] >= l) t++;
		return t;
	}
};

inline void Get_IP(){
	memset(ip,0,sizeof(ip));
	for (int j=1,sta=9;j<=4;j++,sta+=8) {
		int TP = read(), i = 1;
		while (TP) ip[sta-i] = TP & 1, TP >>= 1, i++; 
	}
}

int main(){
	for (int k=read(),a,b;k;k--) {
		char c=getchar(); 
		while (c != 'A' && c != 'Q') c=getchar();
		if (c == 'A') {
			Get_IP();
			TRE::modify(read());
		} else {
			Get_IP();
			a = read(); b = read();
			printf("%d\n",TRE::query(a,b));
		}
	}
	return 0;
}

【Codeforces 706C】Hard problem

题目传送门:http://codeforces.com/contest/706/problem/C
数据生成器:http://paste.ubuntu.com/23048130/

不要问我这道题我做了多久QAQ

一开始是想道2-SAT
后来是写SA一直不停TLE
然后是写O(n)的暴力,仍然T到死

最后发现,是strlen的时间复杂度不对QAQ
之前,我一直以为这货是O(1)的
就是先用黑科技搞定尾地址,然后首尾地址减一减
结果查一查,发现这货是O(n)的QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;

const int MAXN = 200000+9;

LL f[2][MAXN];
int pos[MAXN],n,c[MAXN],rev[MAXN],end[MAXN];
char pat[MAXN];

inline bool judge(int p1, int p2, int len, bool sa){
	int w = 0; char *s1 = &pat[p1], *s2 = &pat[p2]; 
	while (w < len && *s1 == *s2) w++, s1++, s2++;
	if ((w == len && sa) || *s1 < *s2) return true; 
	else return false;
}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
	return ret;
}

int main(){
	int len = 0; cin>>n; 
	for (int i=1;i<=n;i++) c[i] = read();
	for (int i=1;i<=n;i++) {
		pos[i] = len+1; gets(pat+len+1);
		len = strlen(pat+pos[i])+pos[i]-1; 
		end[i] = len;
	} 
	for (int i=1;i<=len;i++) pat[len*2-i+1] = pat[i];	
	for (int i=1;i<=n;i++) rev[i] = len*2-end[i]+1; 
	for (int i=1;i<=n;i++) end[i] = end[i] - pos[i] + 1; 
	
	memset(f,0x3f,sizeof(f)); f[0][1] = 0; f[1][1] = c[1];
	for (int i=2,lim=min(end[i],end[i-1]);i<=n;i++,lim=min(end[i],end[i-1])) {
		if (judge(pos[i-1], pos[i], lim, end[i]>=end[i-1])) f[0][i] = min(f[0][i], f[0][i-1]);
		if (judge(pos[i-1], rev[i], lim, end[i]>=end[i-1])) f[1][i] = min(f[1][i], f[0][i-1] + c[i]);
		
		if (judge(rev[i-1], pos[i], lim, end[i]>=end[i-1])) f[0][i] = min(f[0][i], f[1][i-1]);
		if (judge(rev[i-1], rev[i], lim, end[i]>=end[i-1])) f[1][i] = min(f[1][i], f[1][i-1] + c[i]);
	}
	LL vout = min(f[0][n], f[1][n]);
	cout<<(vout<f[0][MAXN-1]?vout:-1)<<endl;
	return 0;
}

【BZOJ 1414】[ZJOI2009] 对称的正方形

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1414

题号“1414”,还真的是把我做得“要死要死的”!做了一整天QAQ
Hash version:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
 
const int MX = 0;
const int MAXN = 2000+9;
unsigned int SEEDX = 37;
unsigned int SEEDY = 137;
 
int n,m; LL vout;
unsigned int px[2000+9],py[2000+9],mat[MAXN][MAXN],f[5][MAXN][MAXN];
 
inline int read(){
    char c=getchar(); int ret=0;
    while (c<'0'||c>'9') c=getchar();
    while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
    return ret;
}
 
inline void init(){
    m = read(); n = read(); vout = (unsigned int)n*m;
    for (int j=1;j<=m;j++) {
        for (int i=1;i<=n*2+1;i++) mat[i][j*2-1] = MX;
        for (int i=1;i<=n;i++) mat[i*2][j*2] = read(), mat[i*2-1][j*2] = MX;
        mat[n*2+1][j*2] = MX;
    } for (int i=1;i<=n*2+1;i++) mat[i][m*2+1] = MX;
    n = n*2+1; m = m*2+1;
}
 
inline void Hash_init(){
    px[0] = 1; for (int i=1;i<=2001;i++) px[i] = SEEDX*px[i-1];
    py[0] = 1; for (int i=1;i<=2001;i++) py[i] = SEEDY*py[i-1];
    for (int i=n,g=1;i;i--,g++) for (int j=1,h=1;j<=m;j++,h++) f[1][i][j] = px[g]*py[h]*mat[i][j] + f[1][i+1][j] + f[1][i][j-1] - f[1][i+1][j-1];
    for (int i=1,g=1;i<=n;i++,g++) for (int j=1,h=1;j<=m;j++,h++) f[2][i][j] = px[g]*py[h]*mat[i][j] + f[2][i-1][j] + f[2][i][j-1] - f[2][i-1][j-1];
    for (int i=1,g=1;i<=n;i++,g++) for (int j=m,h=1;j;j--,h++) f[3][i][j] = px[g]*py[h]*mat[i][j] + f[3][i-1][j] + f[3][i][j+1] - f[3][i-1][j+1];
    for (int i=n,g=1;i;i--,g++) for (int j=m,h=1;j;j--,h++) f[4][i][j] = px[g]*py[h]*mat[i][j] + f[4][i+1][j] + f[4][i][j+1] - f[4][i+1][j+1];
}
 
inline unsigned int Get_Hash(int t, int x1, int y1, int x2, int y2){
    if (t == 1) return (f[1][x1][y1] + f[1][x2+1][y2-1] - f[1][x2+1][y1] - f[1][x1][y2-1]) * px[2001-(n-x1+1)] * py[2001-y1];
    else if (t == 2) return (f[2][x1][y1] + f[2][x2-1][y2-1] - f[2][x2-1][y1] - f[2][x1][y2-1])* px[2001-x1] * py[2001-y1];
    else if (t == 3) return (f[3][x1][y1] + f[3][x2-1][y2+1] - f[3][x2-1][y1] - f[3][x1][y2+1]) * px[2001-x1] * py[2001-(m-y1+1)];
    else return (f[4][x1][y1] + f[4][x2+1][y2+1] - f[4][x2+1][y1] - f[4][x1][y2+1]) * px[2001-(n-x1+1)] * py[2001-(m-y1+1)];
}
 
inline bool judge(int x, int y, int len){
    unsigned int t1 = Get_Hash(1,x,y,x+len,y-len),t2 = Get_Hash(2,x,y,x-len,y-len);
    if (t1 != t2) return false;
    t1 = Get_Hash(3,x,y,x-len,y+len);
    if (t1 != t2) return false;
    t2 = Get_Hash(4,x,y,x+len,y+len);
    if (t1 != t2) return false;
    else return true;
}
 
inline void solve(){
    for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if ((i&1) + (j&1) != 1) {
        int l=2,r=min(min(i-1,j-1),min(n-i,m-j)),ans=0,mid;
        while (l <= r) {
            mid = l + r >> 1;
            if (judge(i,j,mid)) l = mid+1, ans = mid;
            else r = mid-1;
        }vout += ans/2;
    }
}
 
int main(){
    init(); Hash_init(); solve();
    printf("%lld\n",vout);
    return 0;
}  

Manacher version:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
 
const int MAXN = 2000+9;
 
int mat[MAXN][MAXN],n,m,tmp[MAXN],ans[MAXN],que[MAXN],tot,pos[MAXN];
int res_x[MAXN][MAXN],res_y[MAXN][MAXN],sa_y[MAXN][MAXN],sa_x[MAXN][MAXN];
 
inline int read(){
    char c=getchar(); int ret=0;
    while (c<'0'||c>'9') c=getchar();
    while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
    return ret;
}
 
inline void manacher(int lim){
    int MX=0; ans[0] = 0;
    for (int i=1;i<=lim;i++) {
        if (MX+ans[MX] > i) ans[i] = min(MX+ans[MX]-i, ans[MX*2-i]);
        else ans[i] = 0;
        while (tmp[i+ans[i]+1] == tmp[i-ans[i]-1]) ans[i]++;
        if (ans[i]+i > MX+ans[MX]) MX = i;
    }
}
 
inline void DP(int lim){
    tot = 0; pos[0] = 0; int sta = 0;
    for (int i=1;i<=lim;i++) {
        while (tot && ans[i] <= que[tot]) tot--; sta = min(sta, tot);
        que[++tot] = ans[i]; pos[tot] = i; int w = sta;
        while (w < tot && min(que[w],(i-pos[w-1])*2-1) <= min(que[w+1],(i-pos[w])*2-1)) w++;
        sta = w; tmp[i] = min(que[w],(i-pos[w-1])*2-1);
    } 
}

int main(){
    m = read(); n = read();
    for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) mat[i*2][j*2] = read();
    n = n * 2 + 1; m = m * 2 + 1;
     
    for (int i=1;i<=n;i++) {
        for (int j=1;j<=m;j++) tmp[j] = mat[i][j]; tmp[0] = -1; tmp[m+1] = -2;
        manacher(m); 
        for (int j=1;j<=m;j++) sa_y[i][j] = ans[j];
    }
    for (int j=1;j<=m;j++) {
        for (int i=1;i<=n;i++) ans[i] = sa_y[i][j]*2+1;
        DP(n);
        for (int i=1;i<=n;i++) res_x[i][j] = tmp[i];
        for (int i=1;i*2<=n;i++) swap(ans[i],ans[n-i+1]);
        DP(n);
        for (int i=1;i<=n;i++) res_x[i][j] = min(res_x[i][j],tmp[n-i+1]);
    } 
         
    for (int j=1;j<=m;j++) {
        for (int i=1;i<=n;i++) tmp[i] = mat[i][j]; tmp[0] = -1; tmp[n+1] = -2;
        manacher(n); 
        for (int i=1;i<=n;i++) sa_x[i][j] = ans[i];
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++) ans[j] = sa_x[i][j]*2+1;
        DP(m);
        for (int j=1;j<=m;j++) res_y[i][j] = tmp[j];
        for (int j=1;j*2<=m;j++) swap(ans[j],ans[m-j+1]);
        DP(m);
        for (int j=1;j<=m;j++) res_y[i][j] = min(res_y[i][j], tmp[m-j+1]);
    } 
     
    LL vout = (n/2)*(m/2);
    for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if ((i+j) % 2 == 0)
        vout += max(0,(min(res_x[i][j]-1,res_y[i][j]-1)/2)/2);
    printf("%lld\n",vout);
    return 0;
} 

【NOI五连测】[D3T2] 加密

题目传送门:http://pan.baidu.com/s/1qYPBdpU
数据生成器:http://paste.ubuntu.com/19351549/
OJ传送门:http://oi.cdshishi.net:8080/Problem/1719

这道题目,一看,SA的板题嘛!于是很高兴的码了SA
码完之后一对拍,发现,好像常数很大的样子,然而一看数据范围,嗯,一定是O(nlogn)的复杂度
结果,又被卡常了QAQ

说一说SA的做法:
首先在SA上二分,搞出len
然后再次在SA上二分,搞出最小的标号
然后总体复杂度O(6*n*log(n)),于是T了一个点QAQ

再来说一说SAM的做法(QJC说:这不是SAM的一眼题吗QAQ)
两个字串的lcp,是fail树上的lca,于是考虑在lca上染色的话,O(n)即可
另外,此题不是要求的不是后缀关系,是前缀关系,于是我们倒着建即可

SA版本:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 1000000+9;
const int SGZ = 26;
const int INF = 100000000;

char pat[MAXN];
int n;

namespace Suffix_Array{
	#define SA Suffix_Array
	int height[MAXN],sa[MAXN],t1[MAXN],t2[MAXN],bot[MAXN];
	int *tmp,*rank,m,K[MAXN],LEN[MAXN];
	
	inline void ST_Advance(){
		for (int i=1,k,len;i<=n;i++) {
			k = 0, len = 1;
			while (len < i) len *= 2, k++;
			K[i] = k; LEN[i] = len;
		}
	}
	
	struct Sparse_Table{
		int MN[MAXN][20];
		
		inline void init(int *arr){
			for (int i=1;i<=n;i++) MN[i][0] = arr[i];
			for (int k=1,lim=2;k<=19;k++,lim=1<<k) for (int i=1;i<=n-lim+1;i++)
				MN[i][k] = min(MN[i][k-1], MN[i+(1<<(k-1))][k-1]);
		}
		
		inline int query(int a, int b){
			if (a > b) swap(a, b); 
			int sta=(b-a+2)/2,k=K[sta],len=LEN[sta];
			return min(MN[a][k], MN[b-len+1][k]);
		}
	}ST_num,ST_hei;
	
	inline void GetHeight(){
		for (int i=1;i<=n;i++) if (rank[i] > 1) {
			int sta = max(0, height[rank[i-1]]-1);
			int p1 = sta + i, p2 = sta + sa[rank[i]-1];
			while (pat[p1++] == pat[p2++]) sta++;
			height[rank[i]] = sta;
		}
	}
	
	inline void build(){
		tmp = t1, rank = t2;
		n = strlen(pat+1); m = SGZ;
		for (int i=1;i<=n;i++) bot[tmp[i]=pat[i]-'a'+1]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=1;i<=n;i++) sa[bot[tmp[i]]--] = i;
		rank[sa[1]] = m = 1;
		for (int i=2;i<=n;i++) 
			if (tmp[sa[i]] != tmp[sa[i-1]]) rank[sa[i]] = ++m;
			else rank[sa[i]] = m;
		
		for (int k=1;k<=n;k*=2) { int T = 0;
			for (int i=n-k+1;i<=n;i++) tmp[++T] = i;
			for (int i=1;i<=n;i++) if (sa[i] > k) tmp[++T] = sa[i]-k;
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--]  = tmp[i];
			
			swap(tmp, rank); rank[sa[1]] = m = 1;
			for (int i=2;i<=n;i++) 
				if (tmp[sa[i]] != tmp[sa[i-1]] || tmp[sa[i]+k] != tmp[sa[i-1]+k]) rank[sa[i]] = ++m;
				else rank[sa[i]] = m; 
			
			if (m >= n) break;
		}
		GetHeight();
		ST_Advance();
		ST_num.init(sa);
		ST_hei.init(height);
	}
	
	inline void solve(){
		for (int w=1,tag=0;w<n;tag=0) {
			int mid, l = 1, r = rank[w] - 1, len = 0, ans = 0;
			while (l <= r) {
				mid = (l + r) / 2;
				if (ST_num.query(rank[w]-mid,rank[w]-1) < w) r = mid-1, len = mid;
				else l = mid+1;
			}
			if (len) ans = ST_hei.query(rank[w]-len+1, rank[w]);
			
			l = 1; r = n - rank[w]; len = 0;
			while (l <= r) {
				int mid = (l + r) / 2;
				if (ST_num.query(rank[w]+1, rank[w]+mid) < w) r = mid-1, len = mid;
				else l = mid+1; 
			} 
			if (len) ans = max(ans, ST_hei.query(rank[w]+1, rank[w]+len));
			
			if (ans) {
				printf("%d ",ans); tag = ans;
				l = 1; r = rank[w]; len = 0; ans = INF;
				while (l <= r) {
					mid = (l + r) / 2;
					if (ST_hei.query(rank[w], rank[w]-mid+1) >= tag) l = mid+1, len = mid;
					else r = mid-1; 
				} 
				if (len) ans = ST_num.query(rank[w]-len,rank[w]-1);
				l = 1; r = n-rank[w]; len = 0;
				while (l <= r) {
					mid = (l + r) / 2;
					if (ST_hei.query(rank[w]+1, rank[w] + mid) >= tag) l = mid+1, len = mid;
					else r = mid-1;			
				}
				if (len) ans = min(ans, ST_num.query(rank[w]+1, rank[w]+len));
				printf("%d ",ans-1); w += tag;
			} else {
				printf("%d %d ",-1,(int)pat[w]);
				w ++;
			}
		}
	}
};

int main(){
	freopen("encrypt.in","r",stdin);
	freopen("encrypt.out","w",stdout);
	scanf("%s",pat+1);
	SA::build();
	SA::solve();
	return 0;
}

SAM版本:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 1000000+9;
const int SGZ = 27;

char pat[MAXN];
int n;

namespace Suffix_Automaton{
	#define SAM Suffix_Automaton
	int ch[MAXN][SGZ],fail[MAXN],tag[MAXN],W=1;
	int cnt,last,dep[MAXN],pos[MAXN];
	
	inline void extend(int i, int c){
		int w = last; dep[pos[i]=last=++cnt] = dep[w]+1; 
		while (w && !ch[w]) ch[w] = last, w = fail[w];
		if (!ch[w]) fail[last] = 1;
		else {
			int nw = ch[w];
			if (dep[nw] == dep[w] + 1) fail[last] = nw;
			else {
				int nnw = ++cnt; dep[nnw] = dep[w] + 1;
				memcpy(ch[nnw],ch[nw],sizeof(ch[nw]));
				fail[nnw] = fail[nw]; fail[nw] = fail[last] = nnw;
				while (ch[w] == nw) ch[w] = nnw, w = fail[w];
			}
		}
	}
	
	inline void sign(int w, int val) {
		while (w && !tag[w]) 
			tag[w] = val, w = fail[w];
		if (W == val) {
			if (w <= 1) printf("-1 %d ",(int)pat[val]), W++;
			else printf("%d %d ",dep[w],tag[w]-1), W += dep[w]; 
		}
	}
	
	inline void build(char *s){
		n = strlen(s+1); cnt = last = 1;
		for (int i=n;i;i--) extend(i, pat[i]-'a'+1);
		for (int i=1;i<=n;i++) sign(pos[i], i);
	}
};

int main(){
	scanf("%s",pat+1);
	SAM::build(pat);
	return 0;
} 

看看SA将近4k的代码
再看看SAM只有1.2k的代码
SA,你还要我怎么爱你QAQ

【NOI六连测】[D2T3] 圈圈

题目传送门:http://pan.baidu.com/s/1pLvQmx1
离线版题目:http://paste.ubuntu.com/18302981/
数据传送门:http://pan.baidu.com/s/1dF0ovZj

这一道题,std是后缀树,然而被YYY踩了,居然一个Hash / SA就可捉QAQ
还是说正事吧。
对于每一次++,分两种情况讨论:
1)有至少一个数变成0:
对于这一种情况呢,明显,字典序最小的循环串的开头,只会在这些变成0的位置中产生
所以问题就变为:比较一些给定的字符串的大小。这个看起来也不是很能做的样子。
但如果我们只考虑两两比较,那么去掉这两个串的公共前缀,我们只需要比较一个字符就可以确定大小
所以拿SA / Hash处理出公共前缀后,比较单个字符的大小即可
2)没有数变成零:
对于这一种情况,答案不会变。水涨船高嘛!
于是这个题目就搞定啦!哎,这么简单!我这个纸张考试的时候怎么没有想到呢?QAQ
%%%YYY, Hash踩标程

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN = 200000+9;
const int SGZ = 51000;

int n,m,k,N,M,arr[MAXN],ord[MAXN],now,tot,ans;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if (c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline bool cmp_sort(const int a, const int b){
	return arr[a] > arr[b];
}

namespace Suffix_Array{
	#define SA Suffix_Array
	int sa[MAXN],height[MAXN],t1[MAXN],t2[MAXN],bot[MAXN];
	int *tmp,*rank,m;
	
	namespace Sparse_Table{
		#define ST Sparse_Table
		int MN[MAXN][20];
		
		inline void init(){
			for (int i=1;i<=n;i++) MN[i][0]=height[i];
			for (int k=1;k<=16;k++)	for (int i=1;i<=n;i++)
				MN[i][k] = min(MN[i][k-1],MN[i+(1<<k-1)][k-1]); } inline int query(int l, int r){ if (l > r) swap(l,r); l++;
			int k=0,len=1,L=(r-l+1);
			while (len <= L/2) len*=2,k++;
			return min(MN[l][k],MN[r-len+1][k]);
		}
	};
	
	inline void GetHeight(){
		for (int i=1;i<=n;i++) if (rank[i]>1){
			int sta = max(0,height[rank[i-1]]-1);
			int p1=i+sta, p2=sa[rank[i]-1]+sta;
			while (arr[p1++]==arr[p2++]) sta++;
			height[rank[i]] = sta;
		}
	}
	
	inline void build(){
		tmp=t1; rank=t2; m=SGZ;
		for (int i=1;i<=m;i++) bot[i] = 0;
		for (int i=1;i<=n;i++) bot[tmp[i]=arr[i]+1]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=n;i;i--) sa[bot[tmp[i]]--] = i;
		rank[sa[1]] = m = 1;
		for (int i=2;i<=n;i++)
			rank[sa[i]] = (tmp[sa[i]]==tmp[sa[i-1]])?m:++m;
			
		for (int k=1;k<=n;k*=2){ int T=0;
			for (int i=n-k+1;i<=n;i++) tmp[++T] = i;
			for (int i=1;i<=n;i++) if (sa[i] > k) tmp[++T] = sa[i]-k;
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--] = tmp[i];
			
			swap(tmp,rank);
			rank[sa[1]] = m = 1;
			for (int i=2;i<=n;i++) if (tmp[sa[i]]==tmp[sa[i-1]]&&tmp[sa[i]+k]==tmp[sa[i-1]+k]) rank[sa[i]]=m; else rank[sa[i]] = ++m; if (m >= n) break;
		}
		GetHeight();
		ST::init();
	}
	
	inline int query(int a, int b){
		return ST::query(rank[a],rank[b]);
	}
};

int main(){
	freopen("cyclic.in","r",stdin);
	freopen("cyclic.out","w",stdout);
	scanf("%d%d%d",&N,&M,&k);
	for (int i=1;i<=N;i++) ord[i]=i,
		arr[i]=arr[i+N]=read();
	n = N*2; now = M-1; tot=1; SA::build();
	sort(ord+1,ord+1+N,cmp_sort);
	
	for (int i=1;i<=n;i++) if (SA::sa[i]<=N) {ans=SA::sa[i];break;}
	printf("%d\n",arr[ans+k-1]);
	for (int i=1;i<M;i++,now--){
		if (tot <= N && arr[ord[tot]]==now){
			ans = ord[tot++];
			while (tot <= N && arr[ord[tot]]==now){ int same = SA::query(ans, ord[tot]); int t = (arr[ans+same]+i)%M-(arr[ord[tot]+same]+i)%M; if (t >= 0) ans = ord[tot]; tot++;
			} printf("%d\n",(arr[ans+k-1]+i)%M);
		} else printf("%d\n",(arr[ans+k-1]+i)%M);	
	}
	
	return 0;
} 

【NOI六连测】[D2T1] 矩阵

题目传送门:http://pan.baidu.com/s/1pLvQmx1
离线版题目:http://paste.ubuntu.com/18292275/
数据传送门:http://pan.baidu.com/s/1dFDf8p7

哎呀,第一次看到这个题,一脸懵逼。之前在POJ上倒是做过矩阵的KMP,但这货没有模式串啊!
但是乱搞了一阵之后,如有神助般想到了二分+Hash,时间复杂度O(n^2*log(n))嗯,刚刚好!
于是就开始码Hash,码完之后不放心,还写了一个O(n^4)的SA来对拍,然后满心欢喜,终于可以A题啦!
然后被卡T了 QAQ, 只有暴力分QAQ, 然后今天就爆炸了,这题写了4h+啊!60分滚粗

下面说一点正事,这题正解也是Hash,那么能体现出优劣的就是Hash的方式了
Ⅰ.Sparse_Table版本http://paste.ubuntu.com/18292055/
在考试的时候YY出来的,提前处理出以每一个点为右上角、边长为2的整次方幂的Hash值
然后任意一个矩阵都可以用4个矩阵拼起来
但是预处理是O(n^2*log(n))的时间复杂度
所以大的点全部跑了1.09-1.27s不等,全部被判T。然而std最大的点都跑了0.6s+啊!被卡常了( ▼-▼ )
Ⅱ.前缀和版本http://paste.ubuntu.com/18292096/
这个是std的算法。统计二维的前缀和,然后每一维单独乘以一个以幂的级数递增的质数
然后要提取矩阵的时候,前缀和减一减,然后统一次数即可。于是预处理只需要O(n^2)即可
Ⅲ.常规矩阵Hashhttp://paste.ubuntu.com/18292122/
之前两个算法都可以做到随机访问。这个Hash方式不行。
它是把x轴和y轴分开Hash,然后滑动窗口,虽然不能做到O(1)的随机访问,但是遍历的话,还是可以做到O(1)的
总结:三种Hash方式应该都是不错的,但从时间复杂度和代码的优美程度来讲,我以后会选择第二种

另外,Hash都会涉及到判重的问题,一般来讲,大家都是使用set
但这一次lcr给我提供了一个很好的解决方案:sort+unique
理论复杂度一样,但实测效果比set快了3-4倍
有图有真相:matrix_set_versionmatrix_unique_version

贴一份前缀Hash+unique的版本:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define UL unsigned long long 
using namespace std;

const int MAXN = 500+9;
const int N = MAXN*MAXN;
const UL P = 131313131;
const UL Q = 49999;

int n,m,lim; char pat[MAXN];
UL mat[MAXN][MAXN],PP[MAXN*2],QQ[MAXN*2],tmp[N];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}	

inline UL GetHash(int x, int y, int L){
	UL ret = 0;
	ret = mat[x][y] + mat[x+L][y+L];
	ret -= mat[x+L][y] + mat[x][y+L];
	return ret*PP[1000-y]*QQ[1000-x];
}

inline void init(){
	m = read(); n = read();
	UL p=P,q; lim = min(n,m);
	for (int j=1;j<=m;j++){
		scanf("%s",pat+1);	
		q = Q;
		for (int i=1;i<=n;i++,q*=Q)
			mat[i][j] = (UL)pat[i]*q*p;
		p *= P;
	}
	for (int i=n;i;i--) for (int j=m;j;j--)
		mat[i][j] += mat[i+1][j]+mat[i][j+1]-mat[i+1][j+1];
	QQ[1] = Q; PP[1] = P;
	for (int i=2;i<=1000;i++) 
		QQ[i] = QQ[i-1]*Q,
		PP[i] = PP[i-1]*P;
}

inline bool judge(int L){
	int cnt = 0;
	for (int i=1;i<=n-L+1;i++) 
		for (int j=1;j<=m-L+1;j++)
			tmp[++cnt] = GetHash(i,j,L);
	sort(tmp+1,tmp+1+cnt);
	int tot = unique(tmp+1,tmp+1+cnt) - tmp - 1;
	return tot != cnt;
}

int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	init();
	int l=1,r=lim,mid,vout=0;
	while (l <= r){
		mid = (l+r)/2;
		if (judge(mid)) l = mid+1, vout = mid;
		else r = mid - 1;
	}
	printf("%d\n",vout);
	return 0;
}

【BZOJ 2342】[Shoi2011] 双倍回文

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2342

manacher自己做的第一题! 2A! 撒花! *★,°*:.☆\( ̄▽ ̄)/$:*.°★*
最开始看这个题:树套树?二维偏序的既视感……然而树套树求取区间最值的空间复杂度绝对过不了
于是可耻看题解,瞄了一眼:用set就可以水掉QAQ
于是再自己推一推,确定两个set连起来就可以搞
搞一搞520ms水过去了
再一看题解,还可以只用一个set水过去QAQ
不过跑出来反而比hzwer的一个set快一点……..

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;

const int MAXN = 1200000; 

char pat[MAXN],BUF[MAXN];
int p[MAXN],rt,n,len,vout;

inline void manacher(){
	len = n*2+1; 
	for (int i=1,p1,p2;i<=len;i++){ if (p[rt]+rt > i) p[i] = min(p[rt*2-i],p[rt]+rt-i);
		else p[i] = 0; p1 = i+p[i]; p2 = i-p[i];
		while (pat[++p1] ==  pat[--p2]) p[i]++;
		if (pat[rt]+rt < p[i]+i) rt = i;
	}
}

inline void init(){
	scanf("%d%s",&n,BUF+1);
	for (int i=1;i<=n;i++){
		pat[i*2-1] = '@';
		pat[i*2] = BUF[i];
	} pat[n*2+1] = '@';
	pat[0] = '#'; pat[n*2+2] = '$';
}

struct cmp{
	bool operator () (const int &a, const int &b){
		return a+p[a] < b+p[b];
	}
}; 

set<int> por;
set<int,cmp> list;
set<int>::iterator itr;
set<int,cmp>::iterator ITR;
pair<set<int,cmp>::iterator,bool> TMP;

inline void solve(){
	for (int i=3;i<=len;i+=2){
		int p1 = i-p[i]/2,buf=0;
		itr = por.lower_bound(p1);
		if (itr != por.end()){  
			buf = (i-*itr+1)/2;
			vout = max(vout, buf*4);
		}
			
		TMP = list.insert(i);
		if (TMP.second) por.insert(i);		
		
		ITR = list.begin();
		while (!list.empty() && *ITR+p[*ITR] < i+2) 
			por.erase(*ITR), list.erase(ITR),ITR = list.begin();
	}
	printf("%d",vout);
}

int main(){
	init();
	manacher();
	solve(); 
	return 0;	
} 

【POJ 3693】Maximum repetition substring

相关链接

题目传送门:http://poj.org/problem?id=3693
数据生成器:http://paste.ubuntu.com/18156993/

题目大意

给定长度为$n(n \le 10^5)$,问循环次数最多的子串循环多少次
请输出字典序最小的解

解题报告

这是SA的 论文题 + 神题
而且SAM貌似不可捉的样子 (╯-_-)╯╧╧
求符合要求的串已经是身心具疲,这题还要字典序最小,TM恶心到不行 ( ▼-▼ )

还是说一说怎么求符合要求的串吧:
枚举符合要求的串的长度L,那么符合要求的串一定在s[1],s[1+L],s[1+2*L]…..等位置出现(跟今年APIO的交互题有点像)
于是我们就直接height[] + ST表搞定整节循环的部分,这个地方和KMP差不多
只与前面有多少相同的,看起来不好求,但实际上如果对于答案有贡献,那么一定是补足循环节

然后是如何让字典序最小:
一般的题目没这题这么恶心,这题尤其恶心! MD再掀一次桌子都无法压抑内心的悲伤 (╯-_-)╯╧╧
这个好像只能用SA数组从小到大枚举,然后判断
哎呀,考场上要是遇到这个题,一定做不对QAQ

Code

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 200000+9;
const int SIGMA_SIZE = 26;

char pat[MAXN];

namespace Suffix_Array{
	#define SA Suffix_Array
	int sa[MAXN],height[MAXN],t1[MAXN],t2[MAXN];
	int bot[MAXN],*tmp,*rank,m,n;
	int Tm,len[MAXN],TT,cnt;
	
	namespace Sparse_Table{
		#define ST Sparse_Table
		int MN[MAXN][20];
		
		inline void build(){
			for (int i=1;i<=n;i++) memset(MN[i],0,sizeof(MN[i]));
			for (int i=1;i<=n;i++) MN[i][0] = height[i];
			for (int i=1;i<18;i++){
				for (int j=1;j<=n;j++)
					MN[j][i] = min(MN[j][i-1],MN[j+(1<<(i-1))][i-1]);
			}
		}
		
		inline int query(int l, int r){
			if (r < l) swap(l, r); l++;
			if (l < 1) return -1;
			else {
				int mid=(l+r)/2,k=0;
				while (l+(1<<k)<=mid) k++;
				return min(MN[l][k],MN[r-(1<<k)+1][k]);
			}
		}
	};
	
	inline void GetHeight(){
		for (int i=1;i<=n;i++) if (rank[i] > 1){
			int sta = max(0, height[rank[i-1]]-1);
			int p1 = i+sta, p2 = sa[rank[i]-1]+sta;
			while (pat[p1++]==pat[p2++]) sta++;
			height[rank[i]] = sta;
		}
	}	
	
	inline void build(){
		memset(bot,0,sizeof(bot));
		memset(height,0,sizeof(height));
		tmp = t1; rank = t2;
		n = strlen(pat+1); m = SIGMA_SIZE;
		
		for (int i=1;i<=n;i++) bot[tmp[i]=pat[i]-'a'+1]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=1;i<=n;i++) sa[bot[tmp[i]]--] = i;
		rank[sa[1]] = m = 1;
		for (int i=2;i<=n;i++) rank[sa[i]]=(tmp[sa[i]]==tmp[sa[i-1]])?m:++m;
		
		for (int k=1;k<=n;k*=2){ int T=0;
			for (int i=n-k+1;i<=n;i++) tmp[++T] = i;
			for (int i=1;i<=n;i++) if (sa[i]>k) tmp[++T] = sa[i]-k;
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--] = tmp[i];
			
			swap(rank, tmp);
			rank[sa[1]] = m = 1;
			for (int i=2;i<=n;i++) if (tmp[sa[i-1]]==tmp[sa[i]] && tmp[sa[i-1]+k]==tmp[sa[i]+k]) rank[sa[i]] = m; else rank[sa[i]] = ++m; if (m >= n) break;
		}
		
		GetHeight();
		ST::build();
	}
	
	inline void solve(){
        Tm = 1; 
        for (int k=1;k<=n;k++){
            for (int i=1;i<=n;i+=k){ 
				int sta = ST::query(rank[i],rank[i+k]),T = sta / k + 1; 
				if (sta % k && ST::query(rank[i-k+sta%k],rank[i+sta%k]) >= k-sta%k) T++;
                if (T > Tm){Tm = T; len[cnt=1] = k;}
                if (T == Tm) {len[++cnt] = k;}
            } 
        }
        if (Tm==1) printf("Case %d: %c\n",++TT,pat[sa[1]]);
        else {
            for (int i=1;i<=n;i++) for (int j=1;j<=cnt;j++) {
				if (ST::query(i,rank[sa[i]+len[j]]) >= (Tm-1)*len[j]) {
                    pat[sa[i]+Tm*len[j]] = 0;
                    printf("Case %d: %s\n",++TT,pat+sa[i]); 
                    return;
                }   
			}
        }
    }
};

int main(){
	while (scanf("%s",pat+1) && pat[1] != '#'){
		SA::build();
		SA::solve();
	}
	return 0;
} 

【BZOJ 3998】[TJOI2015] 弦论

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3998
离线版题目:http://paste.ubuntu.com/18145026/
数据生成器:http://paste.ubuntu.com/18145048/

解题报告

这个,SAM求k小串的板题,然而我这个纸张写了两次、调了7h+才过QAQ     (浅谈模板写挂了的悲伤有多大.pdf
t==0 -> 后缀自动机的每一个节点只代表一个串
t==1 -> 后缀自动机的每一个节点代表|fail数的大小|个节点
然后顺着走一走就好。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;

const int MAXN = 1000000+9;
const int SGZ = 26;

char pat[MAXN];
int n,t; LL k;

namespace Suffix_Automaton{
	#define SAM Suffix_Automaton
	int ch[MAXN][SGZ],fail[MAXN],sz[MAXN],len[MAXN];
	int cnt,last,bot[MAXN],ord[MAXN]; LL cho[MAXN];
		
	inline void extend(int c){
		int w = last; last = ++cnt; 
		len[last] = len[w]+1; sz[last]=1;
		while (w && !ch[w]) ch[w] = last, w=fail[w];
		if (!w) fail[last] = 1;
		else {
			int nw = ch[w];
			if (len[nw] == len[w]+1) fail[last] = nw;
			else {
				int nnw = ++cnt; len[nnw] = len[w]+1;
				for (int i=0;i<SGZ;i++) ch[nnw][i]=ch[nw][i];
				fail[nnw] = fail[nw]; fail[nw] = fail[last] = nnw;
				while (w && ch[w]==nw) ch[w] = nnw, w = fail[w];
			}
		}
	}
	
	inline void build(){
		cnt = last = 1;
		n = strlen(pat+1);
		for (int i=1;i<=n;i++)
			extend(pat[i]-'a');
	}
	
	inline void sign(){
		for (int i=1;i<=cnt;i++) bot[len[i]]++;
		for (int i=1;i<=n;i++) bot[i] += bot[i-1];
		for (int i=cnt;i;i--) ord[bot[len[i]]--] = i;
		
		if (t == 1) for (int j=cnt,i=ord[j];j;i=ord[--j]) sz[fail[i]] += sz[i];
		else for (int i=2;i<=cnt;i++) sz[i] = 1;
		
		for (int j=cnt,i=ord[j];j;i=ord[--j]){
			cho[i] += (LL)sz[i];
			for (int c=0;c<SGZ;c++) 
				cho[i] += cho[ch[i]];
		}
	}
	
	inline void solve(){
		if (cho[1] < k) printf("-1\n");
		else {
			int w = 1; while (k > 0){
				for (int c=0;c<SGZ && k>0;c++) 
					if (cho[ch[w]] < k) k -= cho[ch[w]];
					else {putchar(c+'a');w=ch[w];k-=sz[w];break;}
			}
		}	
	}
};

int main(){
	scanf("%s%d%lld",pat+1,&t,&k);
	SAM::build();
	SAM::sign();
	SAM::solve();
	return 0;
}

【BZOJ 3238】[AHOI2013] 差异

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3238
离线版题目:http://paste.ubuntu.com/18084975/
数据生成器:http://paste.ubuntu.com/16074559/

这个东西,想了想,不会做QAQ于是可耻地看题解,果断SA算贡献
最近在做专题,又看到这个题,发现SAM算贡献不是更好算吗QAQ
贴一个SA的代码吧!SAM就偷懒不写啦!o( ̄▽ ̄)ブ

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define SA Suffix_Array
using namespace std;

const int MAXN = 1000000+9;

namespace Suffix_Array{
	int sa[MAXN],bot[MAXN],t1[MAXN],t2[MAXN],height[MAXN],*rank,*tmp;
	int n,m;
	
	inline void build(char *s){
		n = strlen(s+1); m = 26;
		rank = t1; tmp = t2;
		
		for (int i=1;i<=n;i++) bot[tmp[i]=s[i]-'a'+1]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=1;i<=n;i++) sa[bot[tmp[i]]--] = i;
		rank[sa[1]] = m = 1;
		for (int i=2;i<=n;i++) rank[sa[i]] = s[sa[i]]==s[sa[i-1]]?m:++m;
		
		for (int k=1;k<=n;k*=2){
			int tot = 0; 
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=n-k+1;i<=n;i++) tmp[++tot] = i;
			for (int i=1;i<=n;i++) if (sa[i] > k) tmp[++tot] = sa[i] - k;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--] = tmp[i];
			
			swap(rank, tmp); rank[sa[1]] = m = 1;
			for (int i=2;i<=n;i++) if (tmp[sa[i]] == tmp[sa[i-1]] && tmp[sa[i]+k] == tmp[sa[i-1]+k]) rank[sa[i]] = m; else rank[sa[i]] = ++m; if (m >= n) break;
		}
	}
	
	inline void GetHeight(char *s){
		for (int i=1;i<=n;i++) if (rank[i] > 1) {
			int sta = max(0, height[rank[i-1]]-2);
			int p1 = i+sta, p2 = sa[rank[i]-1]+sta;
			while (s[p1++] == s[p2++]) sta++;
			height[rank[i]] = sta;
		}
	}
};

int L[MAXN],R[MAXN]; LL vout;
int cnt,que[MAXN],pos[MAXN]; 

inline void GetAns(){
	using namespace Suffix_Array;
	for(int i=1;i<=n;i++) vout += (LL)i*(LL)(n-1);
	
	cnt = 0;
	for (int i=1;i<=n;i++){ while (cnt && que[cnt] >= height[i]) cnt--;
		L[i] = pos[cnt]+1;
		que[++cnt] = height[i]; pos[cnt] = i;
	} 
	
	cnt = 0; pos[0] = n+1;
	for (int i=n;i;i--){
		while (cnt && que[cnt] > height[i]) cnt--;
		R[i] = pos[cnt]-1;
		que[++cnt] = height[i]; pos[cnt] = i;
	} 
	
	for (int i=1;i<=n;i++) vout -= 2LL*(LL)(i-L[i]+1)*(LL)(R[i]-i+1)*(LL)height[i];
	printf("%lld",vout);
}

char pat[MAXN];
int main(){
	scanf("%s",pat+1);
	SA::build(pat);
	SA::GetHeight(pat); 
	GetAns(); 
	return 0;
}

【HDU 5442】Favorite Donut

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5442
数据生成器:http://paste.ubuntu.com/18084370/

这题,SA可做,SAM可做,最小表示法可做
然而我的SA对拍怎么都过不了,本地不错,交上去就wa,弃疗

#include<iostream>
#include<cstdio> 
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 1600000+9;
const int SIGMA_SIZE = 30;

char pat[MAXN];
int n;

namespace Suffix_Array{
	#define SA Suffix_Array
	int sa[MAXN],height[MAXN],bot[MAXN];
	int t1[MAXN],t2[MAXN],*tmp,*rank;
	int m,cnt,Pos[MAXN],ord[MAXN],wor[MAXN];
	
	inline void GetHeight(){
		for (int i=1;i<=n;i++) height[i] = 0;
		for (int i=1;i<=n;i++) if (rank[i] > 1){
			int sta = max(0,height[rank[i-1]]-2);
			int p1 = i+sta, p2 = sa[rank[i]-1]+sta;
			while (pat[p1++] == pat[p2++]) sta++;
			height[rank[i]] = sta;
		}
	}
	
	inline void build(){
		memset(bot,0,sizeof(bot));
		memset(height,0,sizeof(height));
		
		tmp = t1; rank = t2;
		n = strlen(pat+1); m = SIGMA_SIZE;
		for (int i=1;i<=n;i++) bot[tmp[i]=pat[i]-'a'+2]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=1;i<=n;i++) sa[bot[tmp[i]]--] = i;
		rank[sa[1]] = m = 1;
		for (int i=2;i<=n;i++)
			if (tmp[sa[i]]==tmp[sa[i-1]]) rank[sa[i]] = m;
			else rank[sa[i]] = ++m;
			
		for (int k=1;k<=n;k*=2){int T=0;
			for (int i=n-k+1;i<=n;i++) tmp[++T] = i;
			for (int i=1;i<=n;i++) if (sa[i]>k) tmp[++T] = sa[i]-k;
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--] = tmp[i];
			
			swap(rank, tmp);
			rank[sa[1]] = m = 1;
			for (int i=2;i<=n;i++) if (tmp[sa[i]]==tmp[sa[i-1]] && tmp[sa[i]+k]==tmp[sa[i-1]+k]) rank[sa[i]] = m; else rank[sa[i]] = ++m; if (m >= n) break;
		}
		
		GetHeight();
	}
	
	inline bool sort_cmp(const int a, const int b){
		if (Pos[a] != Pos[b]) return Pos[a] < Pos[b];
		else return wor[a] < wor[b]; } inline bool judge(int i, int pos){ if ((pos>=1&&pos<=n/4) || (pos>=n/2+2&&pos<=n/2+n/4+1)){ cnt=1; if (pos>=1&&pos<=n/4) Pos[cnt]=pos, wor[cnt]=0; else Pos[cnt]=n/2+n/4+2-pos, wor[cnt]=1; while (height[i--] >= n/4){
				pos = sa[i];
				if (pos>=1&&pos<=n/4) Pos[++cnt]=pos, wor[cnt]=0; else if (pos>=n/2+2&&pos<=n/2+n/4-1) Pos[++cnt]=n/2+n/4+2-pos, wor[cnt]=1;
			}
			
			for (int j=1;j<=cnt;j++) ord[j] = j; sort(ord+1,ord+1+cnt,sort_cmp); printf("%d %d\n",Pos[ord[1]],wor[ord[1]]); return true; } else return false; } inline void solve(){ for (int i=n;i;i--) if (judge(i,sa[i])) break; } }; int main(){ int T; cin>>T;
	while (T--){
		scanf("%d%s",&n,pat+1); 
		for (int i=1;i<=n;i++) pat[n+i] = pat[i]; n *= 2;
		for (int i=1;i<=n;i++) pat[n+i+1] = pat[n-i+1];
		pat[n+1] = 'a'-1; n=n*2+1; pat[n+1] = 0;
		SA::build();
		SA::solve();
	}	
	return 0;
}

【HDU 3518】Boring counting

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3518
数据生成器:http://paste.ubuntu.com/18021072/

这个,我是SA乱搞,时间复杂度O(n^3)QAQ,不过要把我卡到这个时间复杂度的数据貌似不好出
还是说说正解吧:枚举长度,然后height[]数组分组搞一搞,又是论文题QAQ
其实最开始想的是SAM,每个节点记录一个最先出现的位置和最后出现的位置,而且这样的话,貌似可以做到O(n)
但是懒得写啦! 还是上一个SA的乱搞代码就水过了吧!

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 3000+9;

char pat[MAXN]; int n;

namespace Suffix_Array{
	#define SA Suffix_Array
	#define SIGMA_SIZE 26
	int sa[MAXN],bot[MAXN],*tmp,*rank;
	int t1[MAXN],t2[MAXN],height[MAXN],m;
	
	inline void GetHeight(){
		for (int i=1,t,p1,p2;i<=n;i++)if(rank[i]>1){
			t = max(0,height[rank[i-1]]-1);
			p1 = i+t; p2 = sa[rank[i]-1]+t;
			while (pat[p1++]==pat[p2++]) t++;
			height[rank[i]] = t;
		}
	}
	
	inline void build(){
		memset(sa,0,sizeof(sa));
		memset(t1,0,sizeof(t1));
		memset(t2,0,sizeof(t2));
		memset(height,0,sizeof(height));
		memset(bot,0,sizeof(bot));
		
		tmp = t1; rank = t2;
		n = strlen(pat+1); m = SIGMA_SIZE;
		for (int i=1;i<=n;i++) bot[tmp[i]=pat[i]-'a'+1]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=1;i<=n;i++) sa[bot[tmp[i]]--] = i;
		m = rank[sa[1]] = 1;
		for (int i=2;i<=n;i++)
			rank[sa[i]] = (tmp[sa[i]]==tmp[sa[i-1]])?m:++m;
		
		for (int k=1;k<=n;k*=2){int T=0;
			for (int i=n-k+1;i<=n;i++) tmp[++T] = i;
			for (int i=1;i<=n;i++) if (sa[i] > k) tmp[++T] = sa[i]-k;
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--] = tmp[i];
			
			swap(rank, tmp); m = rank[sa[1]] = 1;
			for (int i=2;i<=n;i++)
				if (tmp[sa[i]]==tmp[sa[i-1]] && tmp[sa[i]+k]==tmp[sa[i-1]+k]) rank[sa[i]]=m;
				else rank[sa[i]] = ++m;
			
			if (m >= n) break;
		}	
		GetHeight();
	}
	
	inline bool judge(int w, int len){
		int mn=sa[w]-len, mx=sa[w]+len;
		while (w < n && height[w+1]>=len) 
			if (sa[++w] <= mn || sa[w] >= mx) 
				return true;
		return false;
	}
	
	inline void solve(){
		int vout = 0;
		for (int i=1;i<=n;i++){
			int len=n-sa[i]-height[i]+1;
			for (int j=len;j;j--)
				if (judge(i,j+height[i]))
					{vout += j; break;}
		}
		printf("%d\n",vout);	
	}
};

int main(){
	while (~scanf("%s",pat+1) && pat[1] != '#'){
		SA::build();
		SA::solve();
	}
	return 0;
}

【BZOJ 3325】[Scoi2013] 密码

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3325
离线版题目:http://paste.ubuntu.com/18029355/
数据生成器:http://paste.ubuntu.com/18029394/

最近都在做字符串,所以接触了一点逆向字符串的题目,于是这题就会做啦!
这类给你字符串结构的过程数组,叫你统计方案数/构造符合要求的字符串,说到底就是给了你一堆字符等与不等的关系
而你就需要根据具体数据结构的特点来处理这些关系
比如说这道题,我上周六想了半个小时,一点思路都没有,觉得提供的关系可能是O(n^2)的。
但今天再来想的时候发现,相等关系可以用和manacher时间复杂度一样的整法证到时O(n)的
而不等关系很明显是O(n)的,于是保存好关系,并查集缩一缩点就搞好啦!
基本上1A! 撒花! *★,°*:.☆\( ̄▽ ̄)/$:*.°★*

#include<iostream>
#include<cstdio>
#include<set>
#define SITR set<int>::iterator
using namespace std;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

const int MAXN = 100000+9;
const int SIGMA_SIZE = 26;

int ld[MAXN],lo[MAXN],fa[MAXN];
int n,mx=1,vout[MAXN],col[MAXN];
set<int> Q[MAXN];

inline int find(int w){
	int f=fa[w],tmp;
	while (fa[f] != f) f=fa[f];
	while (w != f) tmp=fa[w],fa[w]=f,w=tmp;
	return f;
}

inline void connect(int a, int b){
	int f1=find(a), f2=find(b);
	if (f1 != f2) fa[f1] = f2;
}

inline void discon(int a, int b){
	Q[fa[a]].insert(fa[b]);
	Q[fa[b]].insert(fa[a]);
}

int main(){
	n = read(); 
	for (int i=1;i<=n;i++) fa[i] = i; 
	for (int i=1;i<=n;i++) ld[i] = (read()-1)/2;
	for (int i=1;i<n;i++)  lo[i] = read()/2;
	
	for (int i=1;i<=n;i++){
		for (int j=max(mx-i+1,1);j<=ld[i];j++)
			connect(i+j,i-j);	
		mx = max(mx, i+ld[i]);
		
		for (int j=max(mx-i+1,1);j<=lo[i];j++)
			connect(i+j,i-j+1);		
		mx = max(mx, i+lo[i]);
	}
	for (int i=1;i<=n;i++)  fa[i]=find(i);
	for (int i=1;i<=n;i++){ if (i-ld[i] > 1 && i+ld[i] < n) discon(i+ld[i]+1,i-ld[i]-1); if (i-lo[i] > 0 && i+lo[i] < n) 
			discon(i+lo[i]+1,i-lo[i]);
	}
	
	for (int i=1;i<=n;i++){
		if (vout[fa[i]]) continue;
		else {
			for (SITR j=Q[fa[i]].begin();j!=Q[fa[i]].end();j++)
				col[vout[*j]] = i;
			for (int j=1;j<=SIGMA_SIZE;j++)
				if (col[j] < i) {vout[fa[i]] = j; break;}
		}
	}
	
	for (int i=1;i<=n;i++) putchar(vout[fa[i]]+'a'-1);
	
	return 0;
}

【BZOJ NOI十连测】KMP

题目传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=3026
离线版题目:http://paste.ubuntu.com/18022623/
数据生成器:http://paste.ubuntu.com/18021204/
大样例答案:http://paste.ubuntu.com/18022708/
大样例:http://paste.ubuntu.com/18022662/

这是一道很好玩的题目!本来以为是字符串处理题目,结果最后是图论QAQ
很明显,nxt[]实际上是给出了一些字符的等于或不等关系。
如果我们把不等关系用边来表示,那么这货就是一个弦图。
证明的话,也很简单:因为所连的边一定是一条fail链上拓展出来的,所以每一次连边,一定会向所有可能建立关系的点连边
这样的话,不仅仅是一个弦图?还是一个完全图?
然后用完美消除序列倒着求方案数乘一乘就好。(参见本博客的文章《弦图的计数问题》)

更进一步,kmp的构造顺序就是反的完美消除序列。
这个的证明建立在上文提到的“一定会向所有可能建立关系的点连边”,即每一次如果加入了点,那么这个点是一个单纯点
所以直接按照数组顺序乘就可以了QAQ

弦图染色代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define LL long long
using namespace std;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

const int MAXN = 200000+9;
const LL MOD = 1000000000+7;

int n,c,cnt,fail[MAXN],p[MAXN],Ord[MAXN];
int T,nxt[MAXN],head[MAXN],to[MAXN];
int tot,mx,used[MAXN],pos[MAXN],ord[MAXN];
queue<int> que[MAXN];

inline void AddEdge(int u, int v){
	to[++T] = v; nxt[T] = head[u]; head[u] = T;
	to[++T] = u; nxt[T] = head[v]; head[v] = T;
}

inline void MCS(){
	tot = 0; mx = 0;
	for (int i=1;i<=n;i++)
		que[0].push(i);
		
	while (tot < n){
		while (!que[mx].empty()&&used[que[mx].front()]) 
			que[mx].pop();
		if (que[mx].empty()) mx--;
		else {
			int w = que[mx].front(); que[mx].pop();
			ord[++tot] = w; used[w] = 1;
			for (int i=head[w];i;i=nxt[i]) if (!used[to[i]]) 
				que[++pos[to[i]]].push(to[i]), 
				mx = max(mx, pos[to[i]]);
		}
	}
}

int main(){
	n = read(); c = read();
	for (int i=2;i<=n;i++) fail[i] = read();
	
	p[1] = cnt = 1;
	for (int i=1,j;i<n;i++){
		if (fail[i+1]) p[i+1]=p[fail[i+1]];
		else {
			int j = fail[i]; p[i+1]=++cnt;
			while (j) {
				if (Ord[p[j+1]] < i+1 ) 
					AddEdge(p[j+1],p[i+1]), Ord[p[j+1]]=i+1; 
				j=fail[j];
			}
			if (Ord[p[j+1]] < i+1 ) 
				AddEdge(p[j+1],p[i+1]), Ord[p[j+1]]=i+1; 
		}
	}
	
	MCS();
	
	LL vout = 1;
	memset(used,0,sizeof(used));
	for (int j=1,i=ord[j],sum;j<=cnt;i=ord[++j]){
		sum = 0; 
		for (int k=head[i];k;k=nxt[k])
			if (used[to[k]]) sum++;
		sum = c-sum; used[i] = 1;
		vout = (vout*(LL)sum)%MOD;
	}
	printf("%lld\n",vout);
	
	return 0;
}

%%% YJQ 的利用kmp特殊性质的神代码:

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const LL MOD = 1000000000+7;
const int MAXN = 200000+9;
int n,m,fail[MAXN],go[MAXN];

int main(){
	cin>>n>>m;
	for (int i=2;i<=n;i++)
		scanf("%d",&fail[i]);
	go[0]=1; int ans=m;
	for (int i=1;i<n;i++){
		if (fail[i+1]) go[i] = go[fail[i]];
		else go[i] = go[fail[i]]+1,
		ans = 1LL*ans*(m-go[fail[i]])%MOD;
	}
	printf("%d\n",ans);
	return 0;
}

【POJ 2778】DNA Sequence

题目传送门:http://poj.org/problem?id=2778
数据生成器:http://paste.ubuntu.com/17964294/

说好久没写AC自动机+矩阵快速幂了,专门找了一道来练练手
果然wa了好久好久,不过欣慰的是AC自动机还勉强能写出来

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;

const LL MOD = 100000;
const int MAXN = 100+9;
const int SIGMA_SIZE = 4;

char pat[MAXN];
int N,m,lim,vout;

struct Matrix{
	LL a[109][109];
	
	Matrix(){}
	Matrix(int val){
		for (int i=1;i<=lim;i++)
			memset(a[i],0,sizeof(a[i]));
		for (int i=1;i<=lim;i++)
			a[i][i] = val;
	}
	
}A(0);

inline void Mul(Matrix &M1, Matrix &M2){
	 Matrix ret(0);
	 for (int i=1;i<=lim;i++)
	 	for (int j=1;j<=lim;j++)
	 		for (int k=1;k<=lim;k++) ret.a[i][j] = (ret.a[i][j]+M1.a[k][j]*M2.a[i][k])%MOD; M1 = ret; } inline void Matrix_pow(Matrix &M, int t){ Matrix tmp(1); while (t) { if (t & 1) Mul(tmp, M); Mul(M,M); t >>= 1;
	}
	M = tmp;
}

namespace AC_Automaton{
	#define AC AC_Automaton
	int ch[MAXN][SIGMA_SIZE],get[MAXN];
	int fail[MAXN],cnt=1;
	int que[MAXN],frt,bak;
	
	inline int ord(char c){
		if (c=='A') return 0;
		else if (c=='T') return 1;
		else if (c=='C') return 2;
		else return 3;
	}
	
	inline void insert(char *s){
		int n = strlen(s+1), w = 1;
		for (int i=1,c;i<=n;i++){
			c = ord(s[i]);
			if (!ch[w]) ch[w] = ++cnt;
			w = ch[w];
		}
		get[w]=1;
	}
	
	inline void GetFail(){
		fail[1] = 1; bak=1; lim = cnt; 
		for (int i=0;i<SIGMA_SIZE;i++) if (ch[1][i]) que[++frt]=ch[1][i], fail[ch[1][i]]=1; while (frt >= bak){
			int w = que[bak++]; if (get[fail[w]]) get[w]=1;
			for (int c=0;c<SIGMA_SIZE;c++)if(ch[w]){ int nw = fail[w]; if (get[w]) get[ch[w]] = 1; while (!ch[nw] && nw>1) nw = fail[nw];
				if (!ch[nw]) fail[ch[w]] = nw;
				else fail[ch[w]] = ch[nw];
				que[++frt] = ch[w]; 
			}
		}
	}
	
	inline void Build_Matrix(){
		que[frt=1]=1; bak=1;
		while (frt >= bak){
			int w = que[bak++];
			for (int c=0;c<SIGMA_SIZE;c++)if(ch[w]){ if (!get[ch[w]]) A.a[ch[w]][w]++, que[++frt] = ch[w]; } else { int nw = fail[w]; while (!ch[nw] && nw>1) nw=fail[nw];
				if (ch[nw]) nw = ch[nw]; 
				if (!get[nw]) A.a[nw][w]++;	
			}
		}
	}
};

int main(){
	scanf("%d%d",&N,&m);
	for (int i=1;i<=N;i++){
		scanf("%s",pat+1);
		AC::insert(pat);
	} 
	AC::GetFail();
	AC::Build_Matrix();
	
	Matrix_pow(A,m); 
	for (int i=1;i<=lim;i++)
		vout = (vout+A.a[i][1])%MOD;
	printf("%d\n",vout);
	
	return 0;
}