【BZOJ 4713】迷失的字符串

相关链接

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

解题报告

这题拿bitset搞一搞就好了
f[i]表示前缀匹配,g[i]表示后缀匹配
然后暴力更新
时间复杂度:$O(\frac{n^2}{64})$

Code

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

const int M = 16;
const int SGZ = 26;
const int N = 30000+9;

int head[N],nxt[N<<1],to[N<<1],col[N<<1],sz[N],hvy[N];
int n,m,tot,beg[N],ed[N],id[N],pool[M],top; char s[N];
bitset<N> vout,ans,ve,vis[SGZ],vs[SGZ],f[M],g[M];

inline int read() {
	char c=getchar(); int f=1,ret=0;
	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;
}

inline void Add_Edge(int u, int v, int c) {
	static int E = 1;
	to[++E] = v; nxt[E] = head[u]; head[u] = E; col[E] = c;
	to[++E] = u; nxt[E] = head[v]; head[v] = E; col[E] = c;
}

void DFS1(int w, int f) {
	sz[w] = 1;
	for (int i=head[w];i;i=nxt[i]) {
		if (to[i] != f) {
			DFS1(to[i], w);
			sz[w] += sz[to[i]];
			if (sz[hvy[w]]<sz[to[i]]) 
				hvy[w] = to[i];
		}
	}
}

inline void init(int w) {
	id[w] = pool[top--];
	f[id[w]].reset();
	g[id[w]] = ve;	
}

inline void solve(int x, int y, int c) {
	int ix = id[x], iy = id[y];
	f[iy] <<= 1; f[iy] &= vis; f[iy] |= vs;
	g[iy] = g[iy] & vis;
	vout |= g[iy];
	g[iy] >>= 1;
	ans |= f[ix] & g[iy];
	ans |= g[ix] & f[iy];
	f[ix] |= f[iy];
	g[ix] |= g[iy];
	pool[++top] = iy;
}

void DFS2(int w, int f) {
	if (hvy[w]) DFS2(hvy[w], w);
	init(w);
	for (int i=head[w];i;i=nxt[i]) if (to[i] == hvy[w]) 
		solve(w, hvy[w], col[i]);
	for (int i=head[w];i;i=nxt[i]) if (to[i] != hvy[w] && to[i] != f) 
		DFS2(to[i], w), solve(w, to[i], col[i]);
} 

int main() {
	for (int i=top=M-1;~i;i--) pool[i] = i;
	for (int i=n=read(),u,v;i>1;i--) {
		u = read(); v = read(); 
		scanf("%s",s);
		Add_Edge(u, v, s[0]-'a');
	}
	for (int i=m=read(),len;i;i--) {
		scanf("%s",s); len = strlen(s); 
		vs[s[0]-'a'][beg[m-i+1]=tot+1] = 1; 
		for (int j=0;j<len;j++) vis[s[j]-'a'][++tot] = 1;
		ve[ed[m-i+1]=tot] = 1; 
	}
	DFS1(1, 1); 
	DFS2(1, 1);
	for (int i=1,tag=0;i<=m;i++,tag=0) {
		if (vout[beg[i]]) tag = 1;
		for (int j=beg[i];j<=ed[i]&&!tag;j++) if (ans[j]) tag = 1;
		puts(tag?"YES":"NO");
	}
	return 0;
}

后记

这题似乎之前听谁说过有点分的做法?
不过想不起怎么做了 _(:з」∠)_
会的神犇可不可以给我讲一讲怎么做啊 QwQ

【UOJ 284】[Goodbye Bingshen] 快乐游戏鸡

相关链接

题目传送门:http://uoj.ac/problem/284
数据生成器:http://paste.ubuntu.com/23987506/
官方题解:http://vfleaking.blog.uoj.ac/blog/2292

解题报告

这题有一点不想写题解 _(:з」∠)_
UOJ的官方题解还是很清楚的吧?
那就偷懒不写辣!

值得一提的是,这题也是用链剖来强行优化复杂度
这个优化技巧已经多次在各种比赛中出现了
很多复杂度跟深度有关的题目都可以拿这货优化

另外,scPointer好劲啊!
都给UOJ出题了!
scPointer是我榜样!

Code

这份代码多了一个$log$
问题不是在链剖那里,而是线段树那里偷懒,多了一个二分

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

const int N = 300000+9;
const int M = 19;

int n,m,tot,head[N],to[N],nxt[N],sz[N],pos[N];
int fa[N][M],val[N][M],hvy[N],dep[N],Val[N];
vector<pair<int,int> > q[N];
LL vout[N],sum[N];

class Segment_Tree{
	int tag[N<<2],cnt;
	LL sum[N<<2],ans_tmp;
	public:
		inline void modify(int l, int r, int v) {
			modify(1, 1, n, l, r, v);
		}
		inline int query(int p) {
			query(1, 1, n, p);
			return ans_tmp;
		}
		inline LL GetSum(int l, int r) {
			ans_tmp = 0;
			GetSum(1, 1, n, l, r);
			return ans_tmp;
		}
	private:
		void push_down(int w, int l, int r) {
			tag[w<<1] = tag[(w<<1)+1] = tag[w];
			int mid = l + r + 1 >> 1;
			sum[w<<1] = (LL)(mid - l) * tag[w];
			sum[(w<<1)+1] = (LL)(r -mid + 1) * tag[w];
			tag[w] = 0;
		}
		void modify(int w, int l, int r, int L, int R, int v) {
			if (L <= l && r <= R) {
				tag[w] = v;
				sum[w] = (LL)(r - l + 1) * v; 
			} else {
				if (tag[w]) push_down(w, l, r);
				int mid = l + r + 1 >> 1;
				if (L < mid) modify(w<<1, l, mid-1, L, R, v);
				if (mid <= R) modify((w<<1)+1, mid, r, L, R, v);
				sum[w] = sum[w<<1] + sum[(w<<1)+1]; 
			}
		}
		void query(int w, int l, int r, int p) {
			if (tag[w] || l == r) {
				ans_tmp = tag[w];
				return;
			} else {
				int mid = l + r + 1 >> 1;
				if (p < mid) query(w<<1, l, mid - 1, p);
				else query((w<<1)+1, mid, r, p);
			}
		}	
		void GetSum(int w, int l, int r, int L, int R) {
			if (L <= l && r <= R) {
				ans_tmp += sum[w];
			} else {
				if (tag[w]) push_down(w, l, r);
				int mid = l + r + 1 >> 1;
				if (L < mid) GetSum(w<<1, l, mid-1, L, R);
				if (mid <= R) GetSum((w<<1)+1, mid, r, L, R);
			}
		}
}SEG;

inline int read() {
	char c=getchar(); int f=1,ret=0;
	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;
}

inline void Add_Edge(int u, int v) {
	static int E = 1;
	to[++E] = v; nxt[E] = head[u]; head[u] = E;
}

void DFS1(int w, int f) {
	sz[w] = 1; fa[w][0] = f; 
	dep[w] = dep[f] + 1;
	val[w][0] = Val[f];
	for (int i=head[w];i;i=nxt[i]) {
		DFS1(to[i], w);
		sz[w] = max(sz[w], sz[to[i]] + 1);
		if (sz[hvy[w]] < sz[to[i]]) 
			hvy[w] = to[i];
	}
}

inline int query(int x, int y) {
	int ret = 0; x = dep[x]; 
	for (int j=M-1;~j;j--) {
		if (dep[fa[y][j]] > x) {
			ret = max(ret, val[y][j]);
			y = fa[y][j];
		}  
	} 
	return ret;
}

inline int find(int l, int r, int v) {
	int ret = l, mid;
	while (l <= r) {
		mid = l + r >> 1; 
		if (SEG.query(mid) < v) ret = mid, l = mid + 1;
		else r = mid - 1;
	} 
	return ret + 1;
}

void DFS2(int w) {
	pos[w] = ++tot;
	if (hvy[w]) DFS2(hvy[w]);
	for (int i=head[w];i;i=nxt[i]) {
		if (to[i] != hvy[w]) {
			DFS2(to[i]);
		}
	} 
	for (int i=head[w];i;i=nxt[i]) {
		if (to[i] != hvy[w]) {
			for (int j=0,p,v;j<sz[to[i]];j++) {
				v = SEG.query(pos[to[i]]+j);
				if (SEG.query(pos[w]+j+1) >= v) continue;
				p = find(pos[w]+j-1, pos[w]+sz[w]-1, v); 
				SEG.modify(pos[w]+j+1, p-1, v);
			}
		}
	}
	for (int i=0,lim=q[w].size(),t,id,mx,p;i<lim;i++) {
		t = q[w][i].first; id = q[w][i].second;
		mx = query(w, t); 
		p = find(pos[w], pos[w]+sz[w]-1, mx) - pos[w]; 
		vout[id] = (LL)p * mx - (p?SEG.GetSum(pos[w], pos[w]+p-1):0LL) + dep[t] - dep[w];
	}
	int p = find(pos[w], pos[w]+sz[w]-1, Val[w]);
	SEG.modify(pos[w], p-1, Val[w]);
}

int main() {
	n = read();
	for (int i=1;i<=n;i++) Val[i] = read();
	for (int i=2;i<=n;i++) Add_Edge(read(), i);
	m = read();
	for (int i=1,s,t;i<=m;i++) {
		s = read(); t = read();
		q[s].push_back(make_pair(t, i));
	}
	DFS1(1, 1);
	for (int j=1;j<M;j++) {
		for (int i=1;i<=n;i++) {
			fa[i][j] = fa[fa[i][j-1]][j-1];
			val[i][j] = max(val[i][j-1],val[fa[i][j-1]][j-1]);
		}
	}
	DFS2(1);
	for (int i=1;i<=m;i++) 
		printf("%lld\n",vout[i]);
	return 0;
}

【BZOJ 4543】[POI2014] Hotel加强版

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4543
神犇题解Ⅰ:http://blog.csdn.net/u010600261/article/details/53576251
神犇题解Ⅱ:http://blog.csdn.net/neither_nor/article/details/51278993

解题报告

设$f[i][j]$为到点$i$距离为$j$的点的个数
设$g[i][j]$表示一堆点到点$i$,各自距离均为$j$的对数
这样的话,我们就可以愉快地DP了,时间复杂度$O(n^2)$

现在将这颗树树链剖分,将重儿子重定义为子树深度最深的点
明显一个点的重儿子到这个点的转移可以直接将数组平移,这个用指针实现是$O(1)$的
现在考虑轻儿子,用轻儿子的数组去更新父亲节点的信息

这样看起来是$O(n^2)$的,但实际上这是$O(n)$的
考虑内存使用了多少:每一条重链的空间等于重链长度,所以空间复杂度为$O(n)$的
考虑每一个申请的空间,只会被用来更新所在重链的最上面那个点的父亲
也就是说每一个数组的单位,只会被访问一次,所以时间复杂度也是$O(n)$的啦!

另外值得一提的是,这种优化方式的普适性较强
除了这道题之外,我还在四道题里见到过这样的优化
比如:UOJ 284