【Codeforces 703D】Mishka and Interesting sum

题目传送门:http://codeforces.com/contest/703/problem/D
中文题面:http://www.cnblogs.com/zqy123/p/5746481.html

这题,第一眼看到就感觉是莫队
然后算一算复杂度感觉能卡过,于是死坑莫队,遂卒
然后看题解,突然想起来以前做过这么一道题,还有分块的解法
不过最简单的做法,可以记录pre[],然后维护区间的unique之后的值
离线的话,一维BIT即可,不离线的话,可以上函数式线段树

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<map>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 1000000+9;

int arr[N],n,m,sum[N],vout[N];
struct Query{
	int l,r,num;
	inline bool operator < (const Query &B) const {
		return r < B.r;
	}
}q[N];
map<int,int> pre;

namespace Fenwick_Tree{
	#define BIT Fenwick_Tree
	#define lowbit(x) ((x)&-(x))
	int v[N];
	
	inline void modify(int i) {
		int tmp = pre[arr[i]]; pre[arr[i]] = i;
		if (tmp) for (int j=tmp;j<=n;j+=lowbit(j)) v[j] ^= arr[i]; 
		for (int j=i;j<=n;j+=lowbit(j)) v[j] ^= arr[i];
	}
	
	inline int query(int l, int r) {
		int ret = 0; l--;
		for (;r;r-=lowbit(r)) ret ^= v[r];
		for (;l;l-=lowbit(l)) ret ^= v[l];
		return ret;
	}
};

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

int main(){
	n = read(); for (int i=1;i<=n;i++) arr[i] = read(); 
	m = read(); for (int i=1;i<=m;i++) q[i].l=read(), q[i].r=read(), q[i].num = i;
	sort(q+1,q+1+m); 
	for (int i=1,cur=1;i<=n;i++) {
		sum[i] = sum[i-1] ^ arr[i]; BIT::modify(i);
		for (;cur <= m && q[cur].r == i;cur++) 
			vout[q[cur].num] = sum[i]^sum[q[cur].l-1]^BIT::query(q[cur].l,q[cur].r);
	}
	for (int i=1;i<=m;i++) printf("%d\n",vout[i]);
	return 0;
}

把BZOJ上那题翻出来了,分块的做法让我们召唤黄学长:http://hzwer.com/3663.html

【Codeforces 708C】Centroids

题目传送门:http://codeforces.com/problemset/problem/708/C

我是函数式线段树+出栈入栈序的做法(其实一个RMQ即可)
但这题可以O(n)DP!记录子树中,最大的,不超过n/2的值是多少

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 800000+9;
const int M = 20000000;
const int INF = 1000000000;

int n,tot[N],ls[N],sig,rs[N],head[N],to[N],nxt[N],vex[N*2];

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 Chair_Tree{
	#define CT Chair_Tree
	int sum[M][2],ch[M][2],cnt,root[N],ans_tmp;
	
	void Add(int p, int &w, int l, int r, int pos, int ty) {
		w = ++cnt; ch[w][0] = ch[p][0]; ch[w][1] = ch[p][1]; 
		sum[w][0] = sum[p][0]; sum[w][1] = sum[p][1]; sum[w][ty]++;
		if (l < r) {
			int mid = l + r + 1 >> 1;
			if (pos < mid) Add(ch[p][0],ch[w][0],l,mid-1,pos,ty);
			else Add(ch[p][1],ch[w][1],mid,r,pos,ty);
		}
	} 
	
	inline void modify(int i, int val) {
		if (val > 0) Add(root[i-1],root[i],1,n,val,1);
		else Add(root[i-1],root[i],1,n,-val,0);
	}
	
	void query(int w, int l, int r, int L, int R, int f, int ty){
		if (L <= l && r <= R) ans_tmp += sum[w][ty]*f;
		else {
			int mid = l + r + 1 >> 1;
			if (L < mid) query(ch[w][0],l,mid-1,L,R,f,ty);
			if (R >= mid) query(ch[w][1],mid,r,L,R,f,ty);
		}
	}
	
	inline int query(int l, int r, int L, int R,int ty) {
		if (l > r) return false;
		ans_tmp = 0;
		query(root[l-1],1,n,L,R,-1,ty);
		query(root[r],1,n,L,R,1,ty);
		return ans_tmp;
	}
	
	inline void Merge(){
		for (int i=1;i<=cnt;i++) 
			sum[i][0] = sum[i][1] - sum[i][0];
	}
};

void DFS(int w, int f) {
	tot[w] = 1; ls[w] = ++sig; 
	for (int i=head[w];i;i=nxt[i]) if (to[i] != f)
		DFS(to[i],w), tot[w] += tot[to[i]];
	rs[w] = ++sig; 
	vex[ls[w]] = tot[w];
	vex[rs[w]] = -tot[w];
}

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

inline bool judge(int w) {
	int MX = 0, vx = 0, MN, ty, sta;
	for (int i=head[w];i;i=nxt[i]) 
		if (ls[w] < ls[to[i]] && ls[to[i]] <= rs[w]) {if (MX < tot[to[i]]) MX = tot[to[i]], vx = to[i], ty = 0;} 
		else {int tmp = n - tot[w]; if (MX < tmp) MX = tmp, vx = to[i], ty = 1;}
	if (MX <= n/2) return 1;
	else {
		sta = n/2;
		MN = MX-n/2;
		if (!ty) return CT::query(ls[vx],rs[vx],MN,sta,1);
		else {
			int tmp = CT::query(1,ls[w]-1,MN,sta,1) + CT::query(rs[w]+1,n*2,MN,sta,1);
			tmp -= CT::query(1,ls[w]-1,MN,sta,0); MN = n - MN; sta = n - sta; swap(MN,sta);
			tmp += CT::query(2,ls[w],MN,sta,0);
			return tmp;
		}
	}
}

int main(){
	n = read(); 
	for (int i=1;i<n;i++) AddEdge(read(),read()); DFS(1,1);
	for (int i=1;i<=n*2;i++) CT::modify(i,vex[i]); CT::Merge();
	for (int i=1;i<=n;i++) printf("%d ",judge(i));
	return 0;
}

【Codeforces 707D】Persistent Bookcase

题目链接:http://codeforces.com/problemset/problem/707/D

这个东西,一开始写了一个严格小于O(n*q*log(q))的算法,结果T到死QAQ
然后看到了这一篇blog:http://h0rnet.hatenablog.com
发现严格O(n*q)的暴力都可过QAQ
然而感觉这样暴力一点都不清真啊!虽然只跑了200ms多一点

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

int n,m,q;

namespace Chair_Tree{
	#define CT Chair_Tree
	const int MAXN = 2000000;
	const int N = 1000+9;
	const int Q = 100000+9;
	
	int ls[MAXN],rs[MAXN],sum[MAXN],TY,ans_tmp,cnt,tmp_root,pr[N],pv[N],vout[Q];
	int root[Q][N]; bool rev[Q][N];
	
	inline void Clone(int p, int w) {
		memcpy(root[w],root[p],sizeof(root[w]));
		memcpy(rev[w],rev[p],sizeof(rev[w]));
		vout[w] = vout[p];
	}
	
	void modify(int p, int &w, int l, int r, int pos){
		w = ++cnt; ls[w] = ls[p]; rs[w] = rs[p]; sum[w] = sum[p];
		if (l < r) {
			int mid = l + r + 1 >> 1;
			if (pos < mid) modify(ls[p],ls[w],l,mid-1,pos);
			else modify(rs[p],rs[w],mid,r,pos);
			sum[w] = sum[ls[w]] + sum[rs[w]];
		} else if (sum[w] + TY == 1) sum[w] ^= 1, ans_tmp = 1;
	}
	
	inline int modify(int i, int j, int ty, int k){
		Clone(k-1,k); ans_tmp = 0; TY = ty^rev[k][i]; 
		modify(root[k][i],tmp_root,1,m,j); root[k][i] = tmp_root;
		if (ty == 1) vout[k] += ans_tmp;
		else vout[k] -= ans_tmp;
		return vout[k];
	}
	
	inline int REV(int i,int k){
		Clone(k-1,k); rev[k][i] ^= 1;
		int tmp = sum[root[k-1][i]], w=tmp;
		if (rev[k-1][i]) tmp = m - tmp;
		else w = m - w; vout[k] += w - tmp;
		return vout[k];
	}	
};

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(){
	cin>>n>>m>>q; 
	for (int i=1,ty,x,y;i<=q;i++) {
		ty = read();
		if (ty == 1) {
			x = read(); y = read();
			printf("%d\n",CT::modify(x,y,1,i));
		} else if (ty == 2) {
			x = read(); y = read();
			printf("%d\n",CT::modify(x,y,0,i));
		} else if (ty == 3) printf("%d\n",CT::REV(read(),i));
		else CT::Clone(read(),i), printf("%d\n",CT::vout[i]);
	} 
	return 0;
}

还有那个离线的版本,真的是一点都不清真啊!

【Codeforces 706D】Vasiliy’s Multiset

题目传送门:http://codeforces.com/contest/706/problem/D

可持久化字典树,解决xor

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

const int MAXN = 7000000;
const int SGZ = 40;

int n;

map<int,int> M;

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 int Get(){
	char c=getchar(); 
	while (c != '+' && c != '-' && c != '?') c=getchar();
	if (c == '+') return 1;
	else if (c == '-') return -1;
	else return 0;
}

namespace Chair_Tree{
	#define CT Chair_Tree
	int ans_tmp,cnt,ch[MAXN][2],buf[SGZ],root,sum[MAXN];
	
	void modify(int pre, int &w, int step, int delta){
		w = ++cnt; sum[w] = sum[pre] + delta; ch[w][0] = ch[pre][0]; ch[w][1] = ch[pre][1];
		if (step <= 31) modify(ch[pre][buf[step]],ch[w][buf[step]],step+1,delta);
	}
	
	inline void modify(int val, int delta){
		for (int i=1;i<=31;i++) buf[31-i+1] = val & 1, val >>= 1;
		modify(root, root, 1, delta); 	
	}
	
	void query(int w, int step){
		if (step <= 31) {
			if (sum[ch[w][buf[step]]]) ans_tmp += 1<<(31-step), query(ch[w][buf[step]],step+1);
			else query(ch[w][buf[step]^1],step+1);
		}
	}
	
	inline int query(int val){
		ans_tmp = 0;
		for (int i=1;i<=31;i++) buf[31-i+1] = (val & 1) ^ 1, val >>= 1;
		query(root, 1); 
		return ans_tmp;
	}
};

int main(){
	n = read(); CT::modify(0,1);
	for (int i=1,tp,val;i<=n;i++) {
		tp = Get(); val = read();
		if (tp == 1) {
			M[val] = M[val] + 1;
			if (M[val] == 1) CT::modify(val,1);
		} else if (tp == -1) {
			M[val] = M[val] - 1;
			if (M[val] == 0) CT::modify(val,-1);
		} else printf("%d\n",CT::query(val));
	}
	return 0;
} 

【UOJ 218】[UNR #1] 火车管理

题目传送门:http://uoj.ac/problem/218
数据生成器:http://paste.ubuntu.com/20456908/

这一道题目,考试的时候,还是想要A掉来着,结果wa得只剩10分QAQ
™我的自定义测试,那么大的数据都没有wa

考试的时候,我的做法是:
单独一棵线段树用来处理询问
然后再用一个二维线段树来维护堆栈
第一维是时间,用来二分最近最近的有效操作
第二维是车站,用来支持二分的查询动作
时间复杂度:O(nlog^2(n))

std的做法是:
任然单独搞一棵线段树来对付询问
对于操作,直接上函数式线段树QAQ
时间复杂度:O(nlogn)

至今都觉得还是我的做法最自然
但他们都说函数式线段树最自然QAQ

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

const int MAXN = 500000+9;

int n,m,ty,last_ans,arr[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+last_ans*ty) % n + 1;
}

namespace Segment_Tree{
	#define SEG Segment_Tree
	const int N = MAXN*4;
	int tag[N],sum[N],tm[N],ans_tmp,L,R,DEL;
	
	inline void push_down(int w, int l, int r, int mid){
		tag[w*2] = tag[w*2+1] = tag[w];
		sum[w*2] = (mid-l)*arr[tag[w]];
		sum[w*2+1] = (r-mid+1)*arr[tag[w]];
		tag[w] = 0;
	}
	
	void query(int w, int l, int r){
		if (L <= l && r <= R) ans_tmp += sum[w];
		else {
			int mid = (l + r + 1) / 2;
			if (tag[w]) push_down(w,l,r,mid);
			if (L < mid) query(w*2,l,mid-1);
			if (R >= mid) query(w*2+1,mid,r);
		}
	}inline int query(int l, int r){ans_tmp=0;L=l,R=r;query(1,1,n);return ans_tmp;}
	
	void Modify(int w, int l, int r){
		if (L <= l && r <= R) tag[w] = DEL, sum[w] = (r-l+1)*arr[DEL];
		else {
			int mid = (l + r + 1) / 2;
			if (tag[w]) push_down(w,l,r,mid);
			if (L < mid) Modify(w*2,l,mid-1);
			if (R >= mid) Modify(w*2+1,mid,r);
			sum[w] = sum[w*2] + sum[w*2+1];
		}
	}inline void modify(int l, int r, int del){L=l,R=r,DEL=del;Modify(1,1,n);}
	
	inline int index(int pos){
		int w = 1, l = 1, r = n, mid;
		while (l < r) {
			if (tag[w]) return tag[w];
			else {
				mid = (l + r + 1) / 2;
				if (pos < mid) w = w*2, r = mid-1;
				else w = w*2+1, l = mid;
			}
		}
		return tag[w];
	}
}; 

namespace Persistent_Segment_Tree{
	#define PST Persistent_Segment_Tree
	const int N = 20000000;
	int tag[N],root[N],ch[N][2],tot,cnt,L,R,DEL;
	
	inline int query(int tm, int pos) {
		if (tm < 1) return 0;
		else {
			int w = root[tm], l=1, r=n, mid;
			while (l < r) {
				if (tag[w]) return tag[w];
				else {
					mid = (l + r + 1) / 2;
					if (pos < mid) w = ch[w][0], r = mid-1;
					else w = ch[w][1], l = mid;
				}
			}
			return tag[w];
		}
	}
	
	inline void push_down(int w){
		for (int i=0;i<=1;i++) if (!ch[w][i]) ch[w][i] = ++cnt;
		for (int i=0;i<=1;i++) tag[ch[w][i]] = tag[w];
		tag[w] = 0;
	}
	
	void Modify(int pre, int &w, int l, int r, int tg){
		w = ++cnt; ch[w][1] = ch[pre][1]; 
		ch[w][0] = ch[pre][0]; tag[w] = tg?tg:tag[pre];
		if (L <= l && r <= R) tag[w] = DEL;
		else {
			int mid = (l + r + 1) / 2; 
			if (L < mid) Modify(ch[pre][0],ch[w][0],l,mid-1,tag[w]);
			else if (tag[w]) ch[w][0] = ++cnt, tag[cnt] = tag[w];
			if (mid <= R) Modify(ch[pre][1],ch[w][1],mid,r,tag[w]);
			else if (tag[w]) ch[w][1] = ++cnt, tag[cnt] = tag[w];
			tag[w] = 0;
		}
	}
	
	inline int modify(int l, int r, int val, bool type){
		L = l, R = r, DEL = val; 
		Modify(root[tot], root[(tot+type)], 1, n, 0);
		tot += type; return tot;
	}
};

int main(){
	scanf("%d%d%d",&n,&m,&ty);
	for (int i=1,type,l,r,del;i<=m;i++){
		scanf("%d",&type);
		if (type == 1) {
			l = read(); r = read();
			if (l > r) swap(l, r);
			printf("%d\n",last_ans=SEG::query(l,r));
		} else if (type == 2) {
			l = read();
			int tmp = SEG::index(l);
			int nv = PST::query(tmp-1,l);
			PST::modify(l,l,nv,0);
			SEG::modify(l,l,nv);
		} else {
			l = read(); r = read(); scanf("%d",&del);
			if (l > r) swap(l, r); 
			int tmp = PST::modify(l,r,PST::tot+1,1);
			arr[tmp] = del;
			SEG::modify(l, r, tmp);
		}
	}
	return 0;
}

【NOI五连测】[D2T2] 取名字

题目传送门:http://pan.baidu.com/s/1hsLh92W
OJ传送门:http://oi.cdshishi.net:8080/Problem/1713

这一道题目,考试的时候,想了一个小时,完全没有思路,最后搞了30分的暴力QAQ
考试的时候一直在想如何将更改操作应用于硬币上
然而std是用硬币去操作中查询
为什么考试的时候没有想到呢?考试的时候是想到这样反向来搞的,但因为精神不好+懒所以没有深入思考
所以该睡的觉还是要睡的,该开的黑还是要开的

来说一说std的做法:
设一个硬币,比较大的值为mx,较小的值为mn。操作的阈值为v
则操作可以分为3类
1.mn < v
2.mn <= v < mx
3.mx <= v
对于第一类,明显不影响
对于第二类,明显是不管之前状态如何,全部变为mx
对于第三类,就是直接反转
所以我们可以找到最后一个第二类操作,然后查找之后有多少个三类操作即可

至于代码实现方面,我后来写的是BIT+线段树
std是二维线段树
我的要快一点,std空间要少很多,所以还是把std贴出来吧(std建树的时候有一点奇技淫巧)
std:http://paste.ubuntu.com/19496714/
my code:

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

const int MAXN = 100000+9;

int n,m,arr[MAXN],rev[MAXN],val[MAXN],hash[MAXN*3],tot,MX;
vector<int> G[MAXN]; LL vout;

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 lowbit(x) ((x)&-(x))
	#define N 10000000
	int cnt,ch[N][2],sum[N],ans_tmp;
	int q1[MAXN],q2[MAXN],root[MAXN*3];
	
	void modify(int &w, int l, int r, int val, int del){
		if (!w) w = ++cnt; sum[w] += del; 
		if (l < r) {
			int mid = (l + r + 1) / 2;
			if (val < mid) modify(ch[w][0],l,mid-1,val,del);
			else modify(ch[w][1],mid,r,val,del);
		}
	}
	
	inline void modify(int pos, int val, int del){
		for (int i=val;i<=tot;i+=lowbit(i)) 
			modify(root[i],1,m,pos,del);
	}
	
	void query(int w, int l, int r, int lim, int rim){
		if (lim <= l && r <= rim) ans_tmp += sum[w];
		else {
			int mid = (l + r + 1) / 2;
			if (lim < mid) query(ch[w][0],l,mid-1,lim,rim); if (rim >= mid) query(ch[w][1],mid,r,lim,rim); 
		}
	}
	
	inline int query(int pos, int l, int r){
		ans_tmp = 0; 
		for (int i=pos;i;i-=lowbit(i)) 
			query(root[i],1,m,l,r);
		return ans_tmp;
	}
	
	inline int GetMX(int L, int R) {
		int t1=0,t2=0,l=1,r=m,mid,tmp=0;
		for (int i=R;i;i-=lowbit(i)) q1[++t1] = root[i];
		for (int i=L;i;i-=lowbit(i)) q2[++t2] = root[i];
		for (int i=1;i<=t1;i++) tmp += sum[q1[i]];
		for (int i=1;i<=t2;i++) tmp -= sum[q2[i]];
		if (!tmp) return 0;
		else while (l < r) {
			mid = (l + r + 1) / 2; tmp = 0;
			for (int i=1;i<=t1;i++) tmp += sum[ch[q1[i]][1]];
			for (int i=1;i<=t2;i++) tmp -= sum[ch[q2[i]][1]]; if (tmp > 0) {
				l = mid;
				for (int i=1;i<=t1;i++) q1[i] = ch[q1[i]][1];
				for (int i=1;i<=t2;i++) q2[i] = ch[q2[i]][1];
			} else {
				r = mid - 1;
				for (int i=1;i<=t1;i++) q1[i] = ch[q1[i]][0];
				for (int i=1;i<=t2;i++) q2[i] = ch[q2[i]][0];
			}
		} return l;
	}
};

inline int find(int w){
	int l=1,r=tot,mid;
	while (l <= r) {
		mid = (l + r) / 2;
		if (hash[mid] < w) l = mid+1; else if (hash[mid] > w) r = mid-1;
		else return mid;;
	}
}

int main(){
	freopen("name.in","r",stdin);
	freopen("name.out","w",stdout); 
	n = read();
	for (int i=1;i<=n;i++) arr[i] = hash[++tot] = read();
	for (int i=1;i<=n;i++) rev[i] = hash[++tot] = read();
	m = read();
	for (int i=1,a,b,c;i<=m;i++) 
		a = read(), b = read(), val[i] = hash[++tot] = read(),
		G[a].push_back(i), G[b+1].push_back(-i);
	sort(hash+1,hash+1+tot);
	tot = unique(hash+1,hash+tot+1)-hash-1;
	for (int i=1;i<=n;i++) arr[i] = find(arr[i]);
	for (int i=1;i<=n;i++) rev[i] = find(rev[i]);
	for (int i=1;i<=m;i++) val[i] = find(val[i]);
	
	for (int k=1;k<=n;k++) {
		for (int i=0;i<(int)G[k].size();i++) { if (G[k][i] > 0) CT::modify(G[k][i],val[G[k][i]],1);
			else CT::modify(-G[k][i],val[-G[k][i]],-1);
		}
		int l=1,r=m,ans=0,mn=min(arr[k],rev[k]),mx=max(arr[k],rev[k]);
		ans = CT::GetMX(mn-1,mx-1);
		if (ans) {
			int tmp = CT::query(tot,ans,m) - CT::query(mx-1,ans,m);
			if (tmp % 2) vout += (LL)hash[mn];
			else vout += (LL)hash[mx];
		} else {
			int tmp = CT::query(tot,0,m) - CT::query(mx-1,0,m);
			if (tmp % 2) vout += (LL)hash[rev[k]];
			else vout += (LL)hash[arr[k]];
		}
	}
	printf("%lld\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六连测】[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