【NOI六连测】[D1T1] 比赛

题目传送门:http://pan.baidu.com/s/1kVjNeeV
离线版题目:http://paste.ubuntu.com/18236489/
数据传送门:http://pan.baidu.com/s/1kUR6kTL

哎呀,绍兴一中的NOIP模拟题,第一眼拿到都一脸懵逼QAQ
想了一会儿发现,不就是总方案数减掉一个三位偏序吗?这个我会!
于是开开心心写完BIT+动态建点的线段树。然后小数据对拍,欸!居然没错 (~ ̄▽ ̄)→))* ̄▽ ̄*)o
然后一跑大数据,哎呀,空间简直炸的飞起! 然后再接下来的一个半小时里一直想办法压空间。
什么unsign short配合char之类的都用上了,然而还是不行QAQ,再加上时间复杂度也不一定过得去,于是弃疗

测完程序,发现CDQ+归并就可以卡过QAQ,然而不会CDQ,于是下午学了一学,还是比较好学的。
然而这还不是高潮!高潮在:这货可以降到一维,然后O(nlogn)轻松过 QAQ
对于任意一个符合条件的(x,y)只会有一种情况(在把A,B,C看成一样的情况下):

A[x] < A[y] 
B[x] > B[y]
C[x] > C[y]

然后不难发现,如果我们把[A,B]&[A,C]拿出来统计一次逆序对的话,这货相当于每一个符合要求的(x,y)被算了2次
于是,跑三遍一维逆序对就好了QAQ 这个能算简单的容斥吗?

BIT + 线段树:http://paste.ubuntu.com/18236197/
CDQ分治:http://paste.ubuntu.com/18236240/
逆序对虐场代码:

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

const int MAXN = 200000+9;

int n,a[MAXN],b[MAXN],c[MAXN],ord[MAXN];
LL vout;

namespace Fenwick_Tree{
	#define BIT Fenwick_Tree
	#define low(x) (x&(-x))
	int arr[MAXN],sum;
	
	inline void init(){
		memset(arr,0,sizeof(arr));
		sum = 0;}
	
	inline void modify(int pos){
		for (int i=pos;i<=n;i+=low(i))
			arr[i] += 1; sum++;
	} 
	
	inline int query(int pos){
		int ret = 0;
		for (int i=pos;i;i-=low(i))
			ret += arr[i];
		return sum-ret;
	}
};

inline void itv(int *A, int *B){
	BIT::init();
	for (int i=1;i<=n;i++) ord[A[i]] = i;
	for (int j=1,i=ord[j];j<=n;i=ord[++j])
		vout += BIT::query(B[i]),
		BIT::modify(B[i]);
}

int main(){
	freopen("contest.in","r",stdin);
	freopen("contest.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
	itv(a,b); itv(b,c); itv(a,c); 
	printf("%lld\n",vout/2LL);
	return 0;
}

另外,这题做的时候,发现对于小的结构体,貌似直接排序比排指针还要快一点QAQ
可能是因为后期指针寻址也要花时间吧!所以以后CDQ就直接结构体写了吧! 有图有真相:
struct_iterator

【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;
} 

【日常小测】再次挑战NPC

题目传送门:http://oi.cyo.ng/wp-content/uploads/2016/06/water_meeting.pdf

一看这题,TM这不今年wc的T1吗?哎呀,还是不会带花树QAQ,哎呀冬令营的时候网络流搞了40分,那就来写网络流吧!
于是开始骚写网络流。 然后写完了发现过不了样例QAQ 然后想了几分钟突然发现:
如果只考虑奇偶的话,这个一个联通块里不最多只有一个半空的筐QAQ
于是最后写了一个并查集。 后来问一问,发现带花树也是可以的,但是只有80分(╯-_-)╯╧╧

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
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 = 400000;
const int INF = 100000000;

int n,m,A[MAXN],B[MAXN],bot[MAXN];
int vout,fa[MAXN],sum[MAXN],str[MAXN];
int Q[MAXN],cnt;

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

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

int main(){
	freopen("cpn.in","r",stdin);
	freopen("cpn.out","w",stdout);
	n = read(); m = read();
	for (int i=1;i<=m;i++) fa[i] = i;
	for (int i=1;i<=n;i++){ 
		A[i] = read(); B[i] = read();
		bot[A[i]]++; connect(A[i],B[i]);
	}	
	for (int i=1;i<=m;i++) find(i);
	for (int i=1;i<=m;i++) if (bot[i]%2)
		{sum[fa[i]]++; str[fa[i]] = i;}
	for (int i=1;i<=m;i++) if (sum[i]%2)
		{vout++; Q[++cnt]=str[i];}
	 
	printf("%d\n",vout);
	for (int i=1;i<=cnt;i++)
		printf("%d\n",Q[i]);
	
	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;
}

【Tsinsen 1393】Palisection

题目传送门:http://www.tsinsen.com/A1393
离线版题目:http://paste.ubuntu.com/17953857/
数据生成器:http://paste.ubuntu.com/17953893/

这一道题目想了很久很久。没有想出来。想统计的量,对于每一个位置是变动的,貌似没有什么数据结构可以维护
于是可耻地看了题解 >︿<
好吧,是在下输了。统计相交的情况比较麻烦,但可以统计不相交的 QAQ
另外提个醒:本题可能会求的回文串的数量,这货最多是O(n)的,有些地方不要忘了开long long或及时取模

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

const int MAXN = 2000000+9;
const LL MOD = 51123987;

char pat[MAXN];
int n;

namespace Palindromic_Tree{
	#define PAM Palindromic_Tree
	#define SIGMA_SIZE 26
	int ch[MAXN][SIGMA_SIZE],sz[MAXN];
	int fail[MAXN],len[MAXN],cnt,last;
	int num[MAXN],rnum[MAXN],ti[MAXN]; 
	LL Sum;
	
	inline int newnode(){
		int ret = ++cnt;
		memset(ch[ret],0,sizeof(ch[ret]));
		len[ret] = fail[ret] = sz[ret] = ti[ret] = 0;
		return ret;
	}
	
	inline void extend(int p, int c){
		int w = last;
		while (pat[p-len[w]-1] != pat[p]) w = fail[w];
		if (!ch[w]){
			int nw = newnode(), k = fail[w];
			while (pat[p-len[k]-1] != pat[p]) k = fail[k];
			len[nw] = len[w]+2; fail[nw] = ch[k]; ch[w] = nw;
			ti[nw] = ti[fail[nw]] + 1; 
		}
		last = ch[w];
		sz[last]++;
	}
	
	inline void build(){
		last = cnt = 1;
		fail[0] = fail[1] = 1;
		len[1] = -1; len[0] = 0;
		for (int i=1;i<=n;i++)
			extend(i,pat[i]-'a'),
			num[i] = ti[last];
		
		last = 1; cnt = -1;
		newnode(); newnode();
		fail[0] = fail[1] = 1;
		len[1] = -1; len[0] = 0;
		for (int i=1;i<=n/2;i++) swap(pat[i],pat[n-i+1]);
		for (int i=1;i<=n;i++) extend(i,pat[i]-'a'), rnum[n-i+1] = (ti[last]+rnum[n-i+2]) % MOD; for (int i=cnt;i>1;i--)
			sz[fail[i]] = (sz[fail[i]]+sz[i]) % MOD,
			Sum = (Sum+sz[i])%MOD;
	}
	
	inline int solve(){
		LL ret = 0;
		for (int i=1;i<n;i++)
			ret = (ret+(LL)num[i]*(LL)rnum[i+1]) % MOD; 
		ret = (Sum*(Sum-1)/2LL)%MOD - ret;
		return int((ret%MOD+MOD)%MOD);
	}
};

int main(){
	scanf("%d%s",&n,pat+1);
	PAM::build();
	printf("%d\n",PAM::solve());
	return 0;
}

【BZOJ 3620】似乎在梦中见过的样子

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3620
离线版题目:http://paste.ubuntu.com/17950176/

求字串的前缀和后缀相等。WTF,这不14年的动物园吗?
又用到KMP的fail_chain就是从大到小遍历原串前缀的前后缀相等长度
然后O(n^2)暴力
1A! 撒花! *★,°*:.☆\( ̄▽ ̄)/$:*.°★*

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

const int MAXN = 15000+9;

char pat[MAXN];
int fail[MAXN],vout,N,k;

inline void solve(char *s, int n){
	fail[0] = fail[1] = 0;
	for (int i=2,j=0;i<=n;i++){
		while(j && s[j+1] != s[i]) j = fail[j];
		if (s[j+1] == s[i]) j++;
		fail[i] = j;
	}
	for (int i=k*2+1,t=i;i<=n;i++,t=i) { while ((fail[t])*2+1 > i) t = fail[t];
		if (fail[t] >= k) vout++; 
	}
}

int main(){
	scanf("%s%d",pat+1,&k);
	N = strlen(pat+1);
	for (int i=1;i<=N-k*2;i++)
		solve(pat+i-1,N+1-i);
	printf("%d\n",vout);
	return 0;
}

其实之前想了一想,貌似SA有希望在O(n)的时间复杂度内解决?
搞出sa[]之后,上下以k为限制扫一扫,统计统计。但这样时间复杂度我只能证到是O(n^2)的,还常数还比KMP大