【BZOJ 4373】算术天才⑨与等差数列

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4373
神犇题解:http://blog.csdn.net/TA201314/article/details/51177011

解题报告

首先,假如一个区间满足以下三个条件那么该区间一定合法:

$\%k$后相等
每一个数互不相同
区间和等于某一特定值

后两个条件都可以用线段树维护
问题就是第一个条件非常麻烦

先来考虑一种简单的Hash方式:记录区间的平方和
这样的话,我们可以$O(1)$计算基准值,$O(\log n)$判断是否等于基准值

但众所周知,Hash是不优雅的
于是我们可以先做一个差分序列,然后维护差分序列相邻两项的$\gcd$
考虑如果所有数的差都是$k$的整数倍,那么这些数$ \bmod \ k$之后肯定就相等啦!

另外的话,区间GCD配合差分的题,也不是第一次见了
之前NOI还考过一道:魔幻棋盘
以后和GCD相关的题目要想一想差分行不行啊!

【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

【BZOJ 4282】慎二的随机数列

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4282
神犇题解:http://blog.csdn.net/PoPoQQQ/article/details/48998981

解题报告

首先我们需要得到一个结论:一定存在一组最优解,使得所有的模糊的位置均在LIS中
现在我们来证明一下这个结论:

考虑如果有一组最优解中存在一个模糊的位置不在最优解中
那么讲这个模糊的位置换成后面的第一个确定位置的数即可,这样不会使答案变劣

于是我们就假设一个确定的位置的前缀中有a个不确定位置
那么我们把该确定位置的值减去a(相当于把那a个不确定位置在值域上留出位置)
然后对确定的串做一次LIS,然后加上不确定位置的个数即可

Code

我写LIS一直用的BIT,但PoPoQQQ大爷的LIS似乎更加优雅
他是直接维护一个最优的LIS,然后每一次lower_bound就可以了!

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

const int N = 200000+9;

int n,m,tot,vout,_hash[N],f[N],arr[N];
char pat[10];

class Fenwick_Tree{
	int mx[N];
	public:
		inline void modify(int p, int v) {
			for (int i=p;i<=tot;i+=lowbit(i))
				mx[i] = max(mx[i], v);
		}
		inline int query(int p) {
			int ret = 0; 
			for (int i=p-1;i;i-=lowbit(i))
				ret = max(ret, mx[i]);
			return ret;
		}
	private:
		inline int lowbit(int x) {
			return x & (-x);
		}
}BIT;

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

int main() {
	n = read();
	for (int i=1;i<=n;++i) {
		scanf("%s",pat+1);
		if (pat[1] == 'K') {
			arr[++tot] = read() - m;
			_hash[tot] = arr[tot];		
		} else ++m;
	}
	n = tot; sort(_hash+1, _hash+1+tot);
	tot = unique(_hash+1, _hash+1+tot) - _hash - 1;
	for (int i=1;i<=n;i++) {
		arr[i] = lower_bound(_hash+1, _hash+1+tot, arr[i]) - _hash;
		f[i] = BIT.query(arr[i]) + 1;
		vout = max(f[i], vout);
		BIT.modify(arr[i], f[i]);
	} 
	printf("%d\n",vout + m);
	return 0;
}

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

【AtCoder】[Regular Contest 068 E] Snuke Line

相关链接

题目传送门:http://arc068.contest.atcoder.jp/tasks/arc068_c
数据生成器:http://paste.ubuntu.com/23948002/
官方题解:https://arc068.contest.atcoder.jp/editorial

解题报告

设一个间隔$d$,一种物品的覆盖区间长度为$L$
若 $L \ge d$ 则一定能取,否则只会被遍历到一次
于是我们就从小到达枚举$d$,同时维护$L$,把$L < d$的扔到$BIT$里就可以啦!
复杂度: $O(n{log^2}n)$,不过因为$BIT$的常数关系,所以跑得飞快

Code

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

const int N = 300000+9;

int n,m;
struct Event{
	int l,r,len;
	inline Event() {}
	inline Event(int _l, int _r) {l = _l; r = _r; len = r - l + 1;}
	inline bool operator < (const Event &B) const {return len < B.len;}
}e[N];

class Fenwick_Tree{
	int cnt[N];
	public:
		inline void modify(int l, int r) {
			for (int i=l-1;i;i-=lowbit(i)) cnt[i]--;
			for (int i=r;i;i-=lowbit(i)) cnt[i]++; 
		}
		inline int query(int p) {
			int ret = 0;
			for (int i=p;i<=m;i+=lowbit(i)) ret += cnt[i];
			return ret;
		}
	private:
		inline int lowbit(int x) {
			return x & -x;
		}
}BIT; 

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

int main() {
	n = read(); m = read();
	for (int i=1,l,r;i<=n;i++) {
		l = read(); r = read();
		e[i] = Event(l, r);
	}
	sort(e+1, e+1+n);
	for (int d=1,p=1,sta=n;d<=m;d++) {
		for (;p<=n&&e[p].len==d-1;p++) {
			--sta;
			BIT.modify(e[p].l, e[p].r);
		}
		int vout = 0;
		for (int i=d;i<=m;i+=d) 
			vout += BIT.query(i);
		printf("%d\n",vout+sta);
	}
	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

【BZOJ 4345】[POI2016] Korale

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4345
神犇题解:http://sr16.com:8081/【bzoj4345】【poi2016】korale/

解题报告

这题看到$k$这么玄学,那就一定是与$k$相关辣!
想一想可不可以用线段树合并做。或者可持久化Treap?
然而似乎都不好做。

我们考虑暴搜的搜索树,如果我们使用$BFS$加上小根堆,显然没有问题
于是我们定义二元组$(i,j)$表示当前集合的权值为$i$,最大的元素编号为$j$
类比搜索树,则转移为 $(i,j) \Rightarrow (i + a[j + 1],j + 1)\& (i + a[j + 1] – a[j],j + 1)$
于是我们就可以求出第$k$小的值$ans$了!

然后我们再来考虑如何输出方案:我们暴搜方案,将结果$>ans$的直接剪掉就可以啦!
考虑这样复杂度为什么是对的,因为 $ \le ans$ 的方案数不会超过$k$个啊!

—————————— UPD 2017.3.22 ——————————
今天考试考了这个题,然后wuvin说直接那k短路来做也是对的
想一想正确性确实很对,不过似乎会炸内存?

【BZOJ 2001】[HNOI2010] City 城市建设

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2001
神犇题解Ⅰ:http://www.cnblogs.com/BLADEVIL/p/3512644.html
神犇题解Ⅱ:http://blog.miskcoo.com/2015/04/bzoj-2001

解题报告

如果没有边权,只需要维护连通性
那么搞一个可以撤销的并查集就可以啦!

现在考虑带边权的情况,我们仍然对时间进行分治
在每一层,我们进行两个操作:

1. Contraction 缩小点的规模

将所有当前分治区间内会被修改的边的边权置为 $-INF$
然后做一遍MST,将此时通过非 $-INF$ 连接的点用并查集缩起来
因为$-INF$边只会有区间长度个,所以每一次分治后,点的规模不会超过分治区间的长度

2. Reduction 缩小边的规模

将会被修改的边的边权置为 $INF$,然后再做一遍MST
将此时不在MST中的边删去,因为保留的边数不会超过点数,所以边的规模也不会超过分治区间的长度

所以时间复杂度写出来长这样: $T(n) = 2T(\frac{n}{2}) + n\log n$
用主定理化简之后,时间复杂度就是: $O(n{\log ^2}n)$

【BZOJ 4358】permu

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4358
神犇题解Ⅰ:http://www.cnblogs.com/clrs97/p/5049090.html
神犇题解Ⅱ:http://www.cnblogs.com/czllgzmzl/p/5644724.html

解题报告

1. 莫队+并查集

首先我们考虑一个简化版本:搞一个莫队,然后搞一个值域线段树
这样的话,我们的复杂度就是 $O({n^{1.5}}logn)$ 的,虽然可能会T,但很好想
现在考虑怎么把$log$去掉:如果莫队那里只存在插入操作,那么我们就可以使用并查集来维护
于是考虑莫队那里只有左端点的移动那里会有删除操作,于是我们每一次把左端点块内那一部分拿出来暴力重构
这样的话,复杂度就神奇地少了一个$log$!总复杂度: $O(n\sqrt n \alpha (n))$ 感觉可以A地样子!

2. KD-Tree

这里就是本题的高能部分!简直是太神啦!_(:з」∠)_
考虑将询问化为二维上的点,然后我们按照点值的大小,将序列的位置一个一个插入
每一次把所有包含他的询问的计数器加一,其他的询问的计数器置零
于是我们的每个询问对应的点上的计数器的历史最大值就是这个询问的答案啦!
时间复杂度:$O(n \sqrt n)$

【BZOJ 4423】[AMPPZ2013] Bytehattan

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4423
神犇题解:http://medalplus.com/?p=2428

解题报告

这个算是套路题吧?
考虑对偶图,两点之间不联通
当且仅当,一个点周围的空白部分全部联通

于是我们转成对偶图之后,用并查集判一判连通性就好
具体来说:删除一条边之前,如果该边相邻的空白区域已经联通则删除该边后两端点不联通
否则不影响连通性

【BZOJ 2877】[NOI2012] 魔幻棋盘

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2877
神犇题解Ⅰ:http://www.cnblogs.com/Randolph87/p/3795729.html
神犇题解Ⅱ:https://blog.sengxian.com/solutions/bzoj-2877

解题报告

首先这货需要有一个结论: $\gcd ({a_1},{a_2}, \cdots ,{a_n}) = \gcd ({a_i}, {a_1} – {a_2},{a_2} – {a_3}, \cdots ,{a_{n – 1}} – {a_n})$
证明的话,想一想辗转相除法就可以啦!
酱紫的话,一维情况我们维护一个差分就可以啦!

现在考虑二维的情况,每一维直接用线段树肯定是不可取的
于是我们考虑答案的表达式: $\gcd (gcd({a_{i,j}},{a_{l,j}} – {a_{l + 1,j}}, \cdots ,{a_{r – 1,j}} – {a_{r,j}}), \cdots ,gcd({a_{i,k}},{a_{l,k}} – {a_{l + 1,k}}, \cdots ,{a_{r – 1,k}} – {a_{r,k}}))$
将每一个 $gcd()$ 的第一项全部提出来,就是 ${\rm{gcd}}({a_{l,j}}, \cdots ,{a_{l,k}})$
这个只有一维,可以用上文提到的情况来解决
剩下的部分每一次修改还是会改到 $O(n)$ 个点
这样是不优雅的,于是考虑在第二维也差分一次的话
那么每一次需要修改的点就只有4个了
于是就可以用二维线段树维护啦!

【BZOJ 4712】洪水

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4712
神犇题解Ⅰ:http://www.cnblogs.com/clrs97/p/6006305.html
神犇题解Ⅱ:http://blog.csdn.net/lych_cys/article/details/53454323

解题报告

考虑每一个节点保存三个值

  1. $w(i)$ 表示第i个点自己的权值
  2. $f(i) = \sum\limits_{j \in son(i)} {d(j)} $
  3. $d(i) = \min (w(i),f(i))$

显然 $d(i)$ 就是答案
于是我们只需要考虑维护 $f(i)$ 即可

考虑每一次修改操作
其一定是改变了到根节点的路径上连续一个区间的 $f(i)$
这个连续的区间满足 $f(i) + delta < w(i)$ 于是可以直接用树链剖分搞区间加就可以了

现在考虑复杂度:

因为 $f(i)$ 是不减的,所以如果一个点的 $f(i)$ 大于了 $w(i)$ 那么便会一直大下去
初始每一个点会贡献一次暴力修改,每一次修改操作也会贡献一次暴力修改

于是均摊下来,单次操作的暴力修改次数就是 $O(1)$ 的了
于是总复杂度:$O((n+m)log^2n)$

【BZOJ 4537】[HNOI2016] 最小公倍数

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4537
神犇题解Ⅰ:http://www.cnblogs.com/clrs97/p/5406018.html
神犇题解Ⅱ:http://blog.csdn.net/heheda_is_an_oier/article/details/51197705

解题报告

考虑只有 ${{\rm{2}}^a}$ 的限制的话
只需要将边和询问排序后依次执行就可以了
具体的话,就是判一判在不在同一个连通块和连通块内的最大值是否符合条件

现在有两个限制,且独立不了的话
那么考虑将 $a$ 分块
边和询问按照 $a$ 所在的块为第一关键字,$b$ 为第二关键字
于是单次询问的加边数量就在 $\sqrt m $ 级别了
于是再搞一个持久化并查集什么的,遇到操作就暴力插入、还原
于是复杂度就是 $O({m^{1.5}}\log n)$ 辣!

【BZOJ 4662】Snow

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4662
神犇题解:http://www.cnblogs.com/ccz181078/p/6291464.html

解题报告

想了两个小时没有想出来
一看题解,居然是姿势优雅的暴力

考虑最开始的区间一定是长这样的

删掉正中间的那个区间之后就变成了这样

发现蓝色框住的区间右端点相同了,绿色框住的区间左端点相同了!
于是两个椭圆框住的区间以后就可以进行区间维护了,不用再一个一个改
复杂度的话,因为暴力修改会进行 $ O(n)$ 次,所以单次修改操作的均摊复杂度为 $ O(\log n)$

具体实现上的话,用线段树支持区间操作
再用一个并查集搞一搞,右端点/左端点相同了就并一起
修改就暴力再并查集上爬一爬就好啦!

【BZOJ 4552】[TJOI2016&HEOI2016] 排序

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4552
神犇题解:http://maghsk.github.io/2016/05/05/20160506-bzoj-4552-tjoi2016heoi2016/

解题报告

之前在BC上看到过这道题:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=680&pid=1004
这就不知道是谁抄谁的了

考虑直接做的话,似乎没法用任何数据结构来维护的样子
于是二分最后的答案 $ p$ ,将小于 $ p$ 的置为0,大于 $ p$ 的搞成1
这样的话,排序操作就变成了区间查询和区间赋值
于是搞一个线段树什么的就可以啦!

【BZOJ 4538】[HNOI2016] 网络

相关链接

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4538
神犇题解:http://krydom.com/bzoj4538/

解题报告

这题我先说一种怪怪的做法 QwQ
考虑使用主席树,第一维是权值(用BIT搞成后缀和),第二维是DFS序
这样的话,对于询问我们可以在第一维的权值上进行二分
对于每一次二分 $ a$ ,我们可以通过判断覆盖该点的区间个数是否等于所有不小于 $ a$ 的区间个数是否相等

这样话,原问题转化为:在主席树上的区间加减问题
这一块的时空复杂度是 $ O(n{\log ^3}n)$ 的
然后考虑上二分的 $ log$ 这样的话,复杂度就是 $ O(n{\log ^4}n)$ 的
这种做法是自己YY的,感觉很有道理的样子
然而懒得写代码了,也不知道对不对

另外的话,再说一说正解吧!
考虑一条路径,说白了就是查询路径上的点的时候不查到该路径
查询非该路径上的点的时候查询到该路径
这时考虑树链剖分的话,该路径对应的 $ {log(n)}$ 个区间应该忽略,其他部分添加上这个路径的信息
因为是一整块区间抠掉 $ {log(n)}$ 个区间,所以剩余部分也只会有 $ {log(n)}$ 个区间
于是树链剖分暴力搞一搞就可以辣!
时空复杂度比之前的做法应该要稍微优越一点