【Timus 1099】Work Scheduling

题目传送门:http://acm.timus.ru/problem.aspx?space=1&num=1099
离线版题目:http://paste.ubuntu.com/19164744/

一般图匹配的板题
然而抄范大神的代码抄错了,调了一个下午QAQ
详细情况见之后的博文吧,会单独写一篇算法笔记的

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

const int MAXN = 1000000;

int n,match[MAXN],fa[MAXN],que[MAXN],fro,bak;
int next[MAXN],mark[MAXN],vis[MAXN];
int T,nxt[MAXN],head[MAXN],to[MAXN];

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 merge(int a, int b){
	int f1 = find(a), f2 = find(b);
	if (f1 != f2) fa[f1] = f2;
}

inline int LCA(int x, int y){
	static int t = 0; t++;
	while (true){
		if (x) {
			x = find(x);
			if (vis[x] == t) return x;
			else {
				vis[x] = t;
				x = next[match[x]];
			}
		} swap(x, y);
	}
}

inline void gather(int a, int p){
	while (a != p){
		int b = match[a], c = next[b];
		if (find(c) != p) next = b;
		if (mark[b] == 2) mark[que[++fro]=b] = 1;
		if (mark == 2) mark[que[++fro]=c] = 1;
		merge(a, b); merge(b, c);
		a = c;
	}
}

inline void Augment(int s){
	for (int i=1;i<=n;i++) next[i]=mark[i]=vis[i]=0, fa[i]=i;
	mark[s] = 1; que[fro=bak=1] = s; 
	while (bak <= fro) {
		int w = que[bak++];
		for (int i=head[w];i;i=nxt[i]){
			if (match[w] == to[i]) continue;
			else if (find(to[i]) == find(w)) continue;
			else if (mark[to[i]] == 2) continue;
			else if (mark[to[i]] == 1) {
				int lca = LCA(w, to[i]);
				if (find(w) != lca) next[w] = to[i];
				if (find(to[i]) != lca) next[to[i]] = w;
				gather(w, lca);
				gather(to[i], lca);
			} else if (!match[to[i]]) {
				next[to[i]] = w;
				for (int u=to[i]; u; ) {
					int v = next[u];
					int fv = match[v];
					match[v] = u; match[u] = v;
					u = fv;
				}
				return;
			} else {
				next[to[i]] = w;
				mark[que[++fro] = match[to[i]]] = 1;
				mark[to[i]] = 2; 
			}
		}
	}
}

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

int main(){
	scanf("%d",&n); int a,b;
	while (~scanf("%d%d",&a,&b)) AddEdge(a, b);
	for (int i=1;i<=n;i++) if (!match[i]) Augment(i);
	
	int vout = 0;
	for (int i=1;i<=n;i++) if (match[i]) vout++;
	printf("%d\n",vout);
	for (int i=1;i<=n;i++) if (match[i] > i) printf("%d %d\n",i,match[i]);
	return 0;
}

另外,此题有坑QAQ
边数巨多,TM还不告诉你是RE,给你说的是TLEQAQ

【CDOJ 79】Playground

题目传送门:http://www.acm.uestc.edu.cn/#/problem/show/79
数据生成器:http://paste.ubuntu.com/18848244/

这个题,并查集搞一搞就过了。
然而后来得知一种二分图匹配的思路,感觉很科学,叉不掉。于是写了写代码。
最后发现,二分图匹配的话,只能保证在满足限制的情况下,得出最少解,并不能保证全部染完,比如十字架

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

const int MAXN = 500;

int fa[MAXN],n,tag[MAXN],vout,que[MAXN],mat[MAXN][MAXN];

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 a, int b){
	int f1 = find(a), f2 = find(b);
	if (f2 != f1) fa[f2] = f1;
}	

inline void solve(int sta){
	int tot = 0, ret = 0;
	for (int i=1;i<=n*2;i++)
		if (fa[i] == sta)
			que[++tot] = i;
	for (int i=1;i<=tot;i++) if (tag[que[i]]){
		int w = que[i]; ret++;
		if (w <= n){ 
			for (int j=1;j<=n;j++)
				if (mat[w][j]) tag[n+j] = 0;
		} else {
			for (int j=1;j<=n;j++)
				if (mat[j][w-n]) tag[j] = 0;
		}
	}
	vout += min(ret, tot-ret);
}

int main(){
	int T; cin>>T;
	while (T--){
		scanf("%d",&n); vout = 0;
		for (int i=1;i<=2*n;i++) fa[i] = i;
		for (int j=1,tmp;j<=n;j++){
			for (int i=1;i<=n;i++){
				scanf("%d",&tmp);
				mat[i][j] = tmp;
				if (tmp == 1) 
					connect(i,j+n), 
					tag[i] = tag[j+n] = 1;
			}
		}	
		for (int i=1;i<=n*2;i++) fa[i] = find(i);
		for (int i=1;i<=n*2;i++) if (fa[i]==i & tag[i])
			solve(fa[i]);
		printf("%d\n",vout);
	}	
	return 0;
} 

【NOI六连测】[D4T2] 最短路

题目传送门:http://pan.baidu.com/s/1eRRG6Wy
离线版题目:http://paste.ubuntu.com/18687179/
数据传送门:http://pan.baidu.com/s/1qYtM8f6
数据生成器:http://paste.ubuntu.com/18687249/

唉,以前在CF上,看过这道题,拿到后,一眼就记得跟线段树有关,然而还是没有做出来QAQ
说一说怎么做:
如果边权很小,那么我们直接跑最短路就好,如果边权再大一点,那我们就写高精度
然而这题有2^100000,这个大概有10000那么多位(DEC),高精度会T,而且空间也开不下
所以只能搞神奇的技巧。
我们那函数式线段树来模拟二进制数组。那么单次修改就是log(n)的时间和空间复杂度
那么,比较大小怎么搞?搞一搞Hash,然后找到第一位不同的地方比较大小,仍然是log(n)
那么怎么进位?单点赋一,区间赋零即可,也为log(n)
然后就是码代码的事情了,我代码只写了一个小时,然而调试了3小时QAQ

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

const int MAXN = 200000+9;
const int MOD = 1000000000+7;
const int HASH = 9999971;
const int SEED = 137;
const int INF = 100000000;

int n,m,T,to[MAXN],head[MAXN],nxt[MAXN],cost[MAXN];
int s,t,done[MAXN],tpw[MAXN];

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

namespace Chair_Tree{
	#define CT Chair_Tree
	#define N 10000000
	#define MX 200000
	int root[MAXN],hash[N],val[N],ch[N][2],MN[N];
	int ans_tmp,cnt;
	
	inline bool cmp(int w1, int w2){
		int l=1,r=MX,mid;
		while (l < r){
			mid = (l+r+1)/2;
			if (hash[ch[w1][1]] != hash[ch[w2][1]] || val[ch[w1][1]] != val[ch[w2][1]]) 
			w1 = ch[w1][1], w2 = ch[w2][1], l = mid;
			else w1 = ch[w1][0], w2 = ch[w2][0], r = mid-1; 
		}	
		return val[w1] > val[w2];
	}
	
	void find(int w, int l, int r, int pos){
		if (!w) ans_tmp = min(ans_tmp, l);
		else if (l >= pos) ans_tmp = min(ans_tmp, MN[w]);
		else if (r >= pos && l < r) {
			int mid = (l+r+1) / 2;
			if (pos < mid) find(ch[w][0], l, mid-1, pos);
			find(ch[w][1], mid, r, pos);
		}
	}inline int find(int w, int pos){ans_tmp = INF;find(w, 1, MX, pos);return ans_tmp;}	
	
	inline void maintain(int w, int l, int r){
		int len = (l+r+1)/2-l; MN[w] = INF;
		hash[w] = (LL)((LL)hash[ch[w][0]]+(LL)SEED*hash[ch[w][1]])*SEED % HASH;
		val[w] = (LL)((LL)val[ch[w][0]] + (LL)val[ch[w][1]]*tpw[len]) % MOD;
		if (ch[w][0]) MN[w] = min(MN[w], MN[ch[w][0]]);
		else MN[w] = min(MN[w], l);
		if (ch[w][1]) MN[w] = min(MN[w], MN[ch[w][1]]);
		else MN[w] = min(MN[w], (l+1+r)/2);
		if (l == r && !val[w]) MN[w] = min(MN[w], l);
	}
	
	void modify(int pre, int &w, int l, int r, int p){
		w = ++cnt; ch[w][0] = ch[pre][0]; ch[w][1] = ch[pre][1];
		if (l == r) hash[w] = 1, val[w] = 1, MN[w] = INF;
		else {
			int mid = (l+r+1) / 2;
			if (p < mid) modify(ch[pre][0], ch[w][0], l, mid-1, p);
			else modify(ch[pre][1], ch[w][1], mid, r, p);
			maintain(w,l,r);
		}
	}
	
	void clear(int pre, int &w, int l, int r, int L, int R){
		int mid = (l+r+1) / 2;
		if (L <= l && r <= R) w = 0;
		else {
			w = ++cnt; ch[w][0] = ch[pre][0]; ch[w][1] = ch[pre][1];
			if (L < mid) clear(ch[pre][0], ch[w][0], l, mid-1, L, R);
			if (R >= mid) clear(ch[pre][1], ch[w][1], mid, r, L, R);
			maintain(w,l,r);
		}  
	}
	
	inline int Add(int rt, int pos){
		int p1 = max(find(rt, pos),pos), ret;
		modify(rt, ret, 1, MX, p1);
		if (p1 > pos) clear(ret, ret, 1, MX, pos ,p1-1);
		return ret;
	}
	
	inline void output(int rt){
		printf("%d\n",val[rt]%MOD);
	}
	
	inline void print_path(){
		int w = t; cout<<t<<endl;
		while (w != s){
			for (int i=head[w];i;i=nxt[i]){
				int tmp = Add(root[to[i]], cost[i]);
				if (cmp(tmp,root[w])^cmp(root[w],tmp)==0) 
					w = to[i], cout<<w<<' '<<val[root[w]]<<endl;
			}
		}
		cout<<s;
	}
	
	void build(int &w, int l, int r){
		w = ++cnt; MN[w] = INF;
		if (l == r) hash[w] = val[w] = 1;
		else {
			int mid = (l+r+1)/2;
			build(ch[w][0], l, mid-1);
			build(ch[w][1], mid, r);
			maintain(w,l,r);
		}
	}inline int build_Tree(){int ret; build(ret,1,MX); return ret;}
};

struct DATA{
	int p,rt; DATA(){}
	DATA(int P, int RT):p(P),rt(RT){}
	bool operator < (const DATA &B) const {
		return CT::cmp(rt, B.rt);
	}
};
priority_queue<DATA> Q;

inline void AddEdge(int a, int b, int c){
	to[++T] = b; nxt[T] = head[a]; head[a] = T; cost[T] = c;
	to[++T] = a; nxt[T] = head[b]; head[b] = T; cost[T] = c;
}

inline void Dijkstra(){
	int tmp = CT::build_Tree();
	for (int i=1;i<=n;i++) CT::root[i] = tmp;
	CT::root[s] = 0;
	Q.push(DATA(s,CT::root[s]));
	
	while (!Q.empty()){
		DATA w = Q.top(); Q.pop();
		if (done[w.p]) continue;
		else if (w.p == t) {
			CT::output(w.rt);return;		
		} else {
			done[w.p] = 1;
			for (int i=head[w.p];i;i=nxt[i]){
				if (done[to[i]]) continue;
				else {
					tmp = CT::Add(w.rt,cost[i]);
					if (CT::cmp(CT::root[to[i]], tmp))
						CT::root[to[i]] = tmp, 
						Q.push(DATA(to[i], tmp));		
				}
			}	
		}
	}
	printf("-1\n");
}

int main(){
	freopen("shortest.in","r",stdin);
	freopen("shortest.out","w",stdout);
	n = read(); m = read();
	for (int i=1,a,b,c;i<=m;i++)
		a=read(), b=read(), c=read(),
		AddEdge(a, b, c+1);
	s = read(); t = read(); tpw[0] = 1; 
	for(int i=1;i<=150000;i++)
		tpw[i] = (LL)tpw[i-1]*2%MOD;
 	
	Dijkstra();
	
	return 0;
}

【NOI六连测】[D1T3] 联盟

题目传送门:http://pan.baidu.com/s/1kVjNeeV
离线版题目:http://paste.ubuntu.com/18671145/
数据传送门:http://pan.baidu.com/s/1qYRC8w8
数据生成器:http://paste.ubuntu.com/18671482/

先来说一说lcr的很好想的做法,分类讨论:
1)联盟的所有城市,有一些在首都的子树外,一些在首都的子树内
2)首都在联盟形成伞形内,且子树中,无联盟的城市
3)首都和联盟完全独立
然后是分类处理:
1)用主席树查一查,这个答案是0
2)树上倍增,再利用主席树查一查
3)直接lca搞一搞
lcr的code:http://paste.ubuntu.com/18671740/

然后是std的算法,分类讨论同lcr,做法稍有不同:
1)这个可以不用主席树查,我们使用DFS序,二分找到小于首都的DFS序最大的一个,然后+1,即可判断
2)这个,我们还是用DFS序,找到小于首都最大的那个,然后+1,那么这两个一定是卡得最紧的两个,然后用LCA更新答案
3)直接lca搞一搞

这两种算法优越在,分来讨论以后,把一个帮派缩成了一个代表城市
std的算法优越在,神奇的DFS序,DFS离得近就是夹得紧
另外这种题目,反正不是分类讨论乱搞,就是分治,以后还是要多推推式子
递归版本:http://paste.ubuntu.com/18670939/
非递归版本:

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

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

int n,T,head[MAXN],to[MAXN],nxt[MAXN],k,dep[MAXN],fa[MAXN][20],LC[MAXN];
int l[MAXN],r[MAXN],tot,cnt[MAXN],que[MAXN],*itr[MAXN],BUF[MAXN],now[MAXN];

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 void AddEdge(int a, int b){
	to[++T] = b; nxt[T] = head[a]; head[a] = T;
	to[++T] = a; nxt[T] = head[b]; head[b] = T;
}

inline void DFS(){
	for (int i=1;i<=n;i++) now[i] = head[i];
	int w = 1; tot = 1;
	while (w) {
		if (!now[w]) {
			r[w] = tot;
			w = fa[w][0];
		} else {
			if (!l[w]) l[w] = ++tot;
			int tmp = to[now[w]]; now[w] = nxt[now[w]];
			if (dep[tmp] != dep[w]+1) continue;
			else w = tmp;
		}
	}
}

inline void BFS(){
	BUF[1] = 1; dep[1] = 1;
	for (int fro=1,bak=1;bak<=fro;bak++){
		int w = BUF[bak];
		for (int i=head[w];i;i=nxt[i]){
			if (!dep[to[i]]) dep[to[i]] = dep[w]+1,
			fa[to[i]][0] = w, BUF[++fro] = to[i];
		}
	}
}

inline bool cmp(const int &A, const int &B){
	return l[A] < l[B];
}

inline int find(int *arr, int ri, int val){
	if (l[arr[1]] >= val) return -1;
	else {
		int li=1,mid,ret;
		while (li <= ri){
			mid = (li+ri)/2;
			if (l[arr[mid]] < val) li=mid+1,ret=mid;
			else ri=mid-1;
		}
		return ret;
	}
}

inline int LCA(int a, int b){
	if (dep[a] < dep[b]) swap(a, b);
	for (int i=18;i>=0;i--)
		if (dep[fa[a][i]] >= dep[b]) 
			a = fa[a][i];
	if (a==b) return a;
	else {
		for (int i=18;i>=0;i--) if (fa[a][i] != fa[b][i])
			a = fa[a][i], b = fa[b][i];
		return fa[a][0];
	}
}	

inline void init_LCA(){
	for (int k=1;k<=18;k++)	for (int i=1;i<=n;i++)
		fa[i][k] = fa[fa[i][k-1]][k-1];
}

inline int DIS(int a, int b){
	int lca = LCA(a, b);
	return dep[a]+dep[b]-2*dep[lca];
}

inline void update(int *arr, int c, int v, int &ans, int lc){
	int lca = LCA(lc, v);
	if (lca == v) ans = min(ans, DIS(lc,v));
	else if (lca != v && lca != lc) ans = min(ans, dep[lc]+dep[v]-2*dep[lca]);
	else {
		int tmp = find(arr,c,l[v]);
		if (tmp==-1){
			if (l[arr[1]] <= r[v] && l[arr] > r[v]) ans = 0;
			else ans = min(ans, DIS(v,LCA(v,arr[1])));
		} else {
			if (tmp < c && l[arr[tmp+1]] <= r[v]) ans = 0;
			else {
				int lca = LCA(v,arr[tmp]);
				ans = min(dep[v]-dep[lca], ans);
				if (tmp < c){
					lca = LCA(v,arr[tmp+1]);
					ans = min(dep[v]-dep[lca], ans);
				}
			}
		}
	}
}

int main(){
	freopen("alliances.in","r",stdin);
	freopen("alliances.out","w",stdout);
	srand(19991226); n = read(); 
	for (int i=1;i<n;i++) AddEdge(read(),read());
	BFS(); DFS(); itr[1] = que; init_LCA();
	k = read(); for (int i=1;i<=k;i++) {
		cnt[i] = read(); 
		itr[i+1] = itr[i]+cnt[i];
		for (int j=1,tmp;j<=cnt[i];j++) itr[i][j] = read();
		int buf = itr[i][1];
		for (int j=2;j<=cnt[i];j++) buf = LCA(buf, itr[i][j]);
		LC[i] = buf; sort(itr[i]+1,itr[i]+cnt[i]+1,cmp);
	}
	
	for (int v,t,ans,Q=read();Q;Q--){
		v = read(); t=read(); ans = INF;
		for (int i=1,w;i<=t;i++){
			w = read(); 
			BUF[i] = itr[w][1];
			update(itr[w], cnt[w], v, ans, LC[w]);
		} 
		if (ans){
			int buf = BUF[1];
			for (int i=2;i<=t;i++) buf = LCA(buf, BUF[i]);
			sort(BUF+1,BUF+t+1,cmp), 
			update(BUF, t, v, ans, buf);
		}
		printf("%d\n",ans);
	}
	
	return 0;
} 

在%原教的代码的时候,发现了一点特殊的技能:
如果我们使用“当前弧”类似的技能,那么我们可以用一个BFS + while()很优雅的代替掉DFS()

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

【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

【日常小测】再次挑战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 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 3676】[Apio2014] 回文串

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3676
离线版题目:http://paste.ubuntu.com/17527319/
数据生成:http://paste.ubuntu.com/17528560/

哎呀,现在是下午5:47,我为了写这道题,现在周末作业只完成了1/3,呵呵呵呵!
来说一说坎坷的历程吧:
1. 10min 确定 SA + manacher
2. 1h 码完代码
3. 6h 调试代码,BZOJ仍然超时QAQ
4. UOJ上发现原题,提交,通过!
讲到这里,我只能说,BZOJ数据好强!
————————————————— 我是华丽丽的分割线,下面才是正文 ———————————————————
解题思路:manacher找出所有本质不同的回文串,sa来查询出现了多少次
被卡原因:SA模板出错,ST表模板出错,manacher的使用还有一点小问题
HINT:
1.网上很多代码都有问题,比如hzwer的就可以被“abbababbbb”卡掉,正解7,hzwer输出8(他的马拉车写错了)
2.BZOJ数据太强,SA + manacher要T,不过UOJ可过(其实在manacher那里分类讨论的话,也是可以卡过的
3.会用SAM的就不要用SA做死啦(或者你要用DC3应该也是可以过的,但是我不会QAQ
4.会用回文自动机的就不要用manacher做死啦!
5.刚才都说了网上的代码可能有错,所以能过就好
6.此题必须不放过任意一个本质不同的回文串,否则正确性有问题,哎呀刚才自己出的那组数据找不到了QAQ
7.答案会爆int
Inspiration:对于manacher来说,本质不同的回文串一定是能使右边界指针向右移的串
AC代码:

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

const int MAXN = 1210000;

char BUF[MAXN],pat[MAXN];
int n,rep[MAXN],LEN[MAXN];
LL vout=1;

inline void init(){
	gets(BUF+1);
	n = strlen(BUF+1);
	for (int i=1;i<=n;i++){
		pat[i*2-1] = 123;
		pat[i*2] = BUF[i];
	} n = n*2+1;
	pat[n]=123; LEN[1] = 0;
	for (int i=2;i<=n;i++)
		LEN[i] = LEN[i>>1]+1;
}

namespace Suffix_Array{
	#define SA Suffix_Array
	#define SIGMA_SIZE 30
	int sa[MAXN],bot[MAXN],t1[MAXN],t2[MAXN],*tmp,*rank;
	int m,height[MAXN];

	namespace Sparse_Table{
		#define ST Sparse_Table
		int mx[MAXN][17];

		inline void init(){
			for (int i=1;i<=n;i++)
				mx[i][0] = height[i];
			for (int i=1,len=1;len<=n;i++,len*=2){
				for (int j=1;j<=n;j++)
					mx[j][i] = min(mx[j][i-1],mx[j+len][i-1]);
			}
		}

		inline int query(int l, int r){
			int t=LEN[r-l+1];
			return min(mx[l][t],mx[r-(1<<t)+1][t]);
		}
	};

	inline void GetHeight(char *s){
		for (int i=1,t=0;i<=n;i++){
			if (t) t--;
			int p1 = i+t, p2 = sa[rank[i]-1]+t;
			while (s[p1++] == s[p2++]) t++;
			height[rank[i]] = t;
		}
		pat[n+1]=125; pat[0]=124;
		ST::init();
	}

	inline void build(char *s){
		m = SIGMA_SIZE; tmp = t1; rank = 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++)
			if (s[sa[i]] != s[sa[i-1]]) rank[sa[i]] = ++m;
			else rank[sa[i]] = m;

		for (int k=1,T=0;k<=n;k*=2,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(s);
	}


	inline int match_right(int s,int len){
		int l = 1, r = n-s,ret=1;
		while (l <= r){
			int mid = (l + r) / 2;
			if (ST::query(s+1,s+mid) >= len) ret = mid, l=mid+1;
			else r = mid-1;
		}
		return ret;
	}

	inline int match_left(int s,int len){
		int l = 1, r = s,ret=1;
		while (l <= r){
			int mid = (l + r) / 2;
			if (ST::query(s-mid+1,s) >= len) ret = mid, l=mid+1;
			else r = mid-1;
		}
		return ret;
	}

	inline int search(int s, int len){
		int ret = 1, pos = rank[s];
		if (height[pos] >= len && pos > 1) ret += match_left(pos,len);
		if (height[pos+1] >= len && pos < n) ret += match_right(pos,len);
		return ret;
	}
};

inline void solve(int i){
	int beg = i-rep[i];
	int t = SA::search(beg,rep[i]*2+1);
	vout = max(vout, (LL)t*(LL)rep[i]);
}

inline void manacher(){
	int itr=0;
	for (int i=1;i<=n;i++){
		if (rep[itr]+itr > i) rep[i] = min(rep[itr*2-i],rep[itr]+itr-i);
		else rep[i] = 0;
		int p1 = i+rep[i], p2 = i-rep[i];
		while (pat[--p2]==pat[++p1])
			rep[i]++, solve(i);
		if (rep[i]+i > rep[itr]+itr) itr = i;
	}
	printf("%lldn",vout);
}

int main(){
	init();
	SA::build(pat);
	manacher();
	return 0;
}

来补一发后缀自动机,这简直是屠场啊!
AC代码:

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

const int MAXN = 300000+9;

char pat[MAXN];

namespace Palindromic_Tree{
	#define PAM Palindromic_Tree
	#define SIGMA_SIZE 26
	int ch[MAXN][SIGMA_SIZE],sz[MAXN],fail[MAXN];
	int n,cnt,last,len[MAXN];

	inline void init(){
		cnt = 1;
		fail[0] = fail[1] = 1;
		len[1] = -1;
	}

	inline void expend(int pos, int c, char *s){
		int cur = last;
		while (s[pos-len[cur]-1] != s[pos]) cur = fail[cur];
		if (!ch[cur]){
			int w = ++cnt, k = fail[cur];
			len[w] = len[cur] + 2;
			while (s[pos-len[k]-1] != s[pos]) k = fail[k];
			fail[w] = ch[k]; ch[cur] = w;
		}
		last = ch[cur];
		sz[last]++;
	}

	inline void build(char *s){
		init();
		n = strlen(s+1);
		for (int i=1;i<=n;i++)
			expend(i,s[i]-'a',s);
	}

	inline LL solve(){
		LL ret = 1;
		for (int i=cnt;i;i--){
			sz[fail[i]] += sz[i];
			ret = max(ret, (LL)len[i]*(LL)sz[i]);
		}
		return ret;
	}
};

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