【数据结构杂谈】

简介:

快要省选了,现在啥也不会的菜鸡最后的挣扎
这个星期给自己的任务是搞懂网络流数据结构(我才不会说我网络流写累了)
这个星期的东西都更在这个贴里了

题目:

一: [AHOI2009] 维护序列
[AHOI2009] 维护序列
代码

#include<iostream>
#include<cstdio>
#define ll long long
#define mid ((l + r) >> 1)

ll n,cnt,t,mod,a[100005];

struct P{ll l,r,v,lazy1,lazy2;P(){lazy1 = 1;}};

inline int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))
	 ch=getchar();
	while(isdigit(ch))
	 x=(x<<3)+(x<<1)+(ch^48),
	  ch=getchar();
	return x;
}

struct Segment{
	P num[(100000) << 2];
	void up(ll now){num[now].v = (num[num[now].l].v + num[num[now].r].v) % mod;}
	int build(ll l,ll r){
		int now = ++cnt;
		if(l == r){
			num[now].v = a[l];
			return now;
		} 
		num[now].l = build(l,mid);
		num[now].r = build(mid + 1,r);
		up(now);
		return now;
	}
	void push(ll now,ll l,ll r){
			ll p = num[now].lazy1;
			ll q = num[now].lazy2;
			if(num[now].l){
			num[num[now].l].lazy1 = num[num[now].l].lazy1 * p % mod;
			num[num[now].l].lazy2 = num[num[now].l].lazy2 * p % mod;
			num[num[now].l].lazy2 = (num[num[now].l].lazy2 + q) % mod;
			num[num[now].l].v = (num[num[now].l].v * p + q * (mid - l + 1)) % mod;
			}			
			if(num[now].r){
			num[num[now].r].lazy1 = num[num[now].r].lazy1 * p % mod;
			num[num[now].r].lazy2 = num[num[now].r].lazy2 * p % mod;
			num[num[now].r].lazy2 = (num[num[now].r].lazy2 + q) % mod;
			num[num[now].r].v = (num[num[now].r].v * p + q * (r - mid)) % mod;
			}
			num[now].lazy1 = 1;
			num[now].lazy2 = 0;
			return;
	}
	void change(ll now,ll l,ll r,ll tl,ll tr,ll p,ll q){
		push(now,l,r);
		if(tl <= l && r <= tr){
			num[now].lazy1 = num[now].lazy1 * p % mod;
			num[now].lazy2 = num[now].lazy2 * p % mod;
			num[now].lazy2 = (num[now].lazy2 + q) % mod;
			num[now].v = (num[now].v * p + q * (r - l + 1)) % mod;
			//std::cout<<l<<" "<<r<<" "<<num[now].v<<std::endl;
			return;
		}
		if(mid >= tl)
		change(num[now].l,l,mid,tl,tr,p,q);
		if(mid < tr)
		change(num[now].r,mid + 1,r,tl,tr,p,q);
		up(now);
		return;
	}
	ll query(ll now,ll l,ll r,ll tl,ll tr){
		//std::cout<<l<<" "<<r<<" "<<num[now].lazy1<<" "<<num[now].lazy2<<" "<<num[now].v<<std::endl;		
		push(now,l,r);
		ll ans = 0;
		if(tl <= l && r <= tr)
		return num[now].v;
		if(mid >= tl)
		ans = (ans + query(num[now].l,l,mid,tl,tr)) % mod;
		if(mid < tr)
		ans = (ans + query(num[now].r,mid + 1,r,tl,tr)) % mod;
		return ans;
	}
}Q;


int main(){
	n = read(),mod = read();
	for(int i = 1;i <= n;++i)
	a[i] = read();
	Q.build(1,n);
	t = read();
	while(t -- ){
		ll opt,l,r,q;
		opt = read();
		if(opt == 1){
			l = read(),r = read(),q = read();
			Q.change(1,1,n,l,r,q,0);
		}
		if(opt == 2){
			l = read(),r = read(),q = read();
			Q.change(1,1,n,l,r,1,q);			
		}
		if(opt == 3){
			l = read(),r = read();
			std::cout<<Q.query(1,1,n,l,r)<<std::endl;
		}	
	}
}

二:小白逛公园
维护区间前缀最大,后缀最大,然后合并就行
查询的时候也可以返回一个线段树类型的东西来信息储存
代码

#include<iostream>
#include<cstdio>
#define ll long long
#define mid ((l + r) >> 1)

int n,m,num[500000];

struct P{
	int l,r,lmax,rmax,ans,sum;
};

struct Segment{
	P t[2 * 500005];
	#define l(a) t[a].l
	#define r(a) t[a].r
	#define lm(a) t[a].lmax
	#define s(a) t[a].sum
	#define rm(a) t[a].rmax
	#define ans(a) t[a].ans	
	int cnt = 0;
	void up(int now){
		lm(now) = std::max(lm(l(now)),s(l(now)) + lm(r(now)));
		rm(now) = std::max(rm(r(now)),s(r(now)) + rm(l(now)));
		ans(now) = std::max(rm(l(now)) + lm(r(now)),std::max(ans(l(now)),ans(r(now))));
		s(now) = s(l(now)) + s(r(now));
	}
	int build(int l,int r){
		int now = ++cnt;
		if(l == r){
			s(now) = num[l];
			lm(now) = num[l];
			rm(now) = num[l];
			ans(now) = num[l];
			return now;
		}
		else{
			l(now) = build(l,mid);
			r(now) = build(mid + 1,r);
			up(now);
		}
		return now;
	}
	void change(int l,int r,int to,int p,int now){
		if(l == r){
			s(now) = p;
			lm(now) = p;
			rm(now) = p;
			ans(now) = p;
			return ;		
		}
		if(to <= mid)
		change(l,mid,to,p,l(now));
		else
		change(mid + 1,r,to,p,r(now));
		up(now);
	}
	P q(int l,int r,int tl,int tr,int now){
		if(tl <= l && r <= tr)return t[now];
		if(tl > mid)
		return q(mid + 1,r,tl,tr,r(now));
		if(tr <= mid)
		return q(l,mid,tl,tr,l(now));
		P x = q(l,mid,tl,tr,l(now)),y = q(mid + 1,r,tl,tr,r(now)),re;
		re.sum = x.sum + y.sum;
		re.lmax = std::max(x.sum + y.lmax,x.lmax);
		re.rmax = std::max(y.sum + x.rmax,y.rmax);
		re.ans = std::max(x.ans,std::max(y.ans,x.rmax + y.lmax));
		return re;
	}
}Q;

int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i)
	scanf("%d",&num[i]);
	int q = Q.build(1,n);
	for(int i = 1;i <= m;++i){
		int opt,x,y;
		scanf("%d%d%d",&opt,&x,&y);
		if(opt == 1){
			if(x > y)
			std::swap(x,y);
			std::cout<<Q.q(1,n,x,y,1).ans<<std::endl;
		}
		if(opt == 2){
			Q.change(1,n,x,y,1);
		}
	}
}//20min写完一发A的感觉真好

三: 【模板】普通平衡树
不放链接了
我平衡树,一窍不通
要恶补
放一个\(splay\)板子吧
\(yyb\)的板子真的好,特别优雅)

#include<iostream>
#include<cstdio>
#define ll long long

const int maxn = 500100,INF = 1e9;

int root = 0,N,tot = 0;

struct P{
	int ch[2];
	int ff;
	int cnt;
	int val;
	int son;
}t[maxn];

void push_up(int u){t[u].son= t[t[u].ch[0]].son + t[t[u].ch[1]].son + t[u].cnt;}//维护子树大小

void rotate(int x){//旋转 
	int y = t[x].ff;
	int z = t[y].ff;
	int k = t[y].ch[1] == x;
	t[z].ch[t[z].ch[1] == y] = x;t[x].ff = z;
	t[y].ch[k] = t[x].ch[k ^ 1];t[t[x].ch[k ^ 1]].ff = y;
	t[x].ch[k ^ 1] = y;t[y].ff = x;
	push_up(y);push_up(x);
}

void splay(int x,int goal){
	while(t[x].ff != goal){
		int y = t[x].ff;
		int z = t[y].ff;
		if(z != goal)
		(t[y].ch[0] == x) ^ (t[z].ch[0] == y) ? rotate(x) : rotate(y);
		rotate(x); 
	}
	if(goal == 0)
	root = x;
} 

void insert(int x){
	int u = root,ff = 0;
	while(u && t[u].val != x){
		ff = u;
		u = t[u].ch[x > t[u].val];
	}
	if(u)
	t[u].cnt ++ ;
	else{
		u = ++tot;
		if(ff)
		t[ff].ch[x > t[ff].val] = u;
		t[tot].ch[0] = 0;
		t[tot].ch[1] = 0;
		t[tot].ff = ff;
		t[tot].val = x;
		t[tot].cnt = 1;
		t[tot].son = 1;
	}
	splay(u,0);
}

void find(int x){
	int u = root;
	if(!u) return;
	while(t[u].ch[x > t[u].val] && x != t[u].val)
	u = t[u].ch[x > t[u].val];
	splay(u,0);
}

int Next(int x,int f){
	find(x);
	int u = root;
	if((t[u].val > x && f) || (t[u].val < x & !f)) return u;
	u = t[u].ch[f];
	while(t[u].ch[f ^ 1]) u = t[u].ch[f ^ 1];
	return u;
}

void del(int x){
	int last = Next(x,0);
	int next = Next(x,1);
	splay(last,0);
	splay(next,last);
	int del = t[next].ch[0];
	if(t[del].cnt > 1){
		t[del].cnt -- ;
		splay(del,0);
	}
	else
	t[next].ch[0] = 0;
}

int k_th(int x){
	int u = root;
	if(t[u].son < x)
	return false;
	while(1){
		int y = t[u].ch[0];
		if(x > t[y].son + t[u].cnt){
			x -= t[y].son + t[u].cnt;
			u = t[u].ch[1];
		}
		else
		if(t[y].son >= x)
		u = y;
		else
		return t[u].val;
	}
}

int main(){
       insert(-2147483647);
       insert(+2147483647);
	scanf("%d",&N);
	while(N -- ){
		int opt;
		int x;
		scanf("%d%d",&opt,&x);
		if(opt == 1)
		insert(x);
		if(opt == 2)
		del(x);
		if(opt == 3){
			find(x);
			std::cout<<t[t[root].ch[0]].son<<std::endl; 
	    }
	    if(opt == 4)
	    std::cout<<k_th(x + 1)<<std::endl;
	    if(opt == 5)
	    std::cout<<t[Next(x,0)].val<<std::endl; 
	    if(opt == 6)
	    std::cout<<t[Next(x,1)].val<<std::endl; 
	}
	return 0;
}

四:【模板】普通平衡树(数据加强版)
折磨了我三天左右,还是不会\(splay\),奋斗了两天没有学会\(splay\)\(Dix\)师傅,拿起了\(fhqtreap\),然后我三十分钟做完了

#include<bits/stdc++.h>
#define ll long long
#define A 2000010

inline ll read(){
	ll ans = 0,f = 1;
	char a = getchar();
	while(a != '-' && (a < '0' || a > '9')) a = getchar();
	if(a == '-') f = -1,a = getchar();
	while(a <= '9' && a >= '0')
	ans = (ans << 3) + (ans << 1) + (a - '0'),a = getchar();
	return f * ans;
} 

ll ch[A][2],val[A],cv[A],siz[A],cnt;
#define l(x) ch[x][0]
#define r(x) ch[x][1]
#define v(x) val[x]
#define c(x) cv[x]
#define s(x) siz[x]

void up(ll x){s(x) = 1 + s(l(x)) + s(r(x));}

ll randoom(){return rand() << 15 | rand();}

ll newcode(ll x){s(++cnt) = 1,v(cnt) = x,c(cnt) = randoom();return cnt;}

void split(ll now,ll k,ll &x,ll &y){
	if(!now){x = y = 0;return;}
	if(v(now) <= k) x = now,split(r(now),k,r(now),y);
	else
	y = now,split(l(now),k,x,l(now));
	up(now);
}

ll merge(ll x,ll y){
	if(!x || !y)return x + y;
	if(c(x) < c(y)){
		r(x) = merge(r(x),y);
		up(x);return x;
	}
	else{
		l(y) = merge(x,l(y));
		up(y);return y;
	}
}

ll root,x,y,z,cn;

void insert(ll a){
	cn ++ ;
	split(root,a,x,y);
	root = merge(merge(x,newcode(a)),y);
}

void del(ll a){
	cn -- ;
	split(root,a,x,z);
	split(x,a - 1,x,y);
	y = merge(l(y),r(y));
	root = merge(x,merge(y,z));
}

ll find(ll a){
	split(root,a - 1,x,y);
	ll ans = s(x) + 1;
	root = merge(x,y);
	return ans;
}

ll kth(ll now,ll k){
	if(k <= s(l(now)))return kth(l(now),k);
	else
	if(k == s(l(now)) + 1)return now;
	else
	return kth(r(now),k - s(l(now)) - 1);
}

ll pre(ll a){
	split(root,a - 1,x,y);
	ll ans = v(kth(x,s(x)));
	merge(x,y);
	return ans;
}

ll nex(ll a){
	split(root,a,x,y);
	ll ans = v(kth(y,1));
	merge(x,y);
	return ans;
}

ll n,m;
ll last,ans;

int main(){
	n = read(),m = read();
	for(int i = 1;i <= n;++i){insert(read());}
	for(int i = 1;i <= m;++i){
		ll opt = read(),a = read() ^ last;
		if(opt == 1)
		insert(a);
		if(opt == 2)
		del(a);
		if(opt == 3)
		ans ^= (last = find(a));
		if(opt == 4)
		ans ^= (last = v(kth(root,a)));
		if(opt == 5)
		ans ^= (last = pre(a));
		if(opt == 6)
		ans ^= (last = nex(a));
	}
	std::cout<<ans<<std::endl;
}


五: [Ynoi2010] y-fast trie
[Ynoi2010] y-fast trie
考虑维护一些最佳匹配\(<x,y>\),\(x + y < c\),我们发现就单纯用\(multiset\)维护这样是\(O(n ^ 2)\)
我们考虑哪些是不用被记录的,假如有两个这样的匹配\(<x,y>,<y,z>,其中z < x\)那么很显然后者是不用被维护的,那么我们可以通过这个方式
排除一些不用维护的最佳匹配,那么我们发现最后这样的最佳匹配只会有\(O(n)\)个,最后在最佳匹配和\(S\)中的最大次大值的和的贡献进行比较即可
所以最后的复杂度是\(O(nlogn)\)

#include <bits/stdc++.h>
using namespace std;
int n,c,sz;
multiset<int> a,b;
multiset<int>::iterator it;
inline int best(int x,int op)
{
	if(x==-1) return -1;
	it=a.upper_bound(c-1-x);
	if(it==a.begin()) return -1;
	it--;
	if(op==1 && *it==x && a.count(x)==1)
		return (it==a.begin())?-1:*--it;
	else
		return *it;
}

inline void insert(int x)
{
	sz++;
	if(sz==1) { a.insert(x); return; }
	int y=best(x,0),z=best(y,1),w=best(z,1);
	if(y!=-1 && z<x)
	{
		if(z!=-1 && y==w) b.erase(b.find(y+z));
		b.insert(x+y);
	}
	a.insert(x);
}
inline void erase(int x)
{
	a.erase(a.find(x)),sz--;
	if(!sz) return;
	int y=best(x,0),z=best(y,1),w=best(z,1);
	if(y!=-1 && z<x)
	{
		if(z!=-1 && y==w) b.insert(y+z);
		b.erase(b.find(x+y));
	}
}
inline int query()
{
	it=--a.end();
	if(a.count(*it)>=2) return *it*2%c;
	else return (*it+*--it)%c;
}
int main()
{
	scanf("%d%d",&n,&c);
	int op,x,lastans=0;
	while(n--)
	{
		scanf("%d%d",&op,&x); x^=lastans;
		if(op==1) insert(x%c);
        else erase(x%c);
		if(sz<2) puts("EE"),lastans=0;
		else printf("%d\n",lastans=max(query(),b.empty()?0:*--b.end()));
	}
	return 0;
}

六:小清新人渣的本愿
小清新人渣的本愿
在写上一个题的时候,我觉得如果不强制在线,那么莫队完全可以使用
于是我在\(Uoj\)问到了这题
莫队,考虑用\(bitset\)维护是否出现,加减就解决了(可以具体看代码实现)
那么乘法怎么解决呢,暴力枚举他的约数,不会出问题的\(O(\sqrt{n})\)
代码(莫队真好写)

#include<bits/stdc++.h>
#define ll long long

ll n,m;

std::bitset<100005>f,g,ans;

ll num[100005],rt[100005];
ll cnt[100005];

struct P{
	ll l,r,id,x,opt;
}q[100005];

bool operator < (P a,P b){
	if(rt[a.l] == rt[b.l])
	return a.r < b.r;
	else
	return rt[a.l] < rt[b.l];
}


void add(ll i){if(cnt[num[i]] ++ == 0)f[num[i]] = 1,g[100005 - num[i]] = 1;}
void del(ll i){if(cnt[num[i]] -- == 1)f[num[i]] = 0,g[100005 - num[i]] = 0;}

int main(){
	scanf("%lld%lld",&n,&m);
	ll s = sqrt(n);
	for(int i = 1;i <= n;++i)
	scanf("%lld",&num[i]),rt[i] = (i - 1) / s + 1;
	for(int i = 1;i <= m;++i){
		scanf("%lld%lld%lld%lld",&q[i].opt,&q[i].l,&q[i].r,&q[i].x);
		q[i].id = i;
	}	
	std::sort(q + 1,q + m + 1);
	ll l = 0,r = 0;
	for(int i = 1;i <= m;++i){
		while(l < q[i].l)del(l++);
		while(l > q[i].l)add(--l);
		while(r < q[i].r)add(++r);
		while(r > q[i].r)del(r--);
		if(q[i].opt == 1){
			if((f & (f << q[i].x)).any())
			ans[q[i].id] = 1;
		}
		if(q[i].opt == 2){
			if((f & (g >> (100005 - q[i].x))).any())
			ans[q[i].id] = 1;
		}
		if(q[i].opt == 3){
			for(int j = 1;j * j <= q[i].x;++j){
				if(!(q[i].x % j))
				if(f[j] && f[q[i].x / j]){
					ans[q[i].id] = 1;
					break;
				}
			}
		}
	}
	for(int i = 1;i <= m;++i)
	ans[i] ? puts("hana") : puts("bi");
}

七:CF438D The Child and Sequence
CF438D The Child and Sequence
三个操作
区间和
单点修改
区间膜
前两个操作都是平凡的,注意到膜这一部分,做过花神游历各国的都知道\(\sqrt{x}\)这个操作是均摊的,那么对于膜这个操作来说一样是均摊的。
考虑\(x % p\),那么知这个\(x\)至少减少了一半,所以是\(log\)级别的,我们只需要在线段树上递归时,加上一个操作就是最大值小于\(p\)时便不再做膜即可
代码

#include<bits/stdc++.h>
#define ll long long 

ll n,m;
ll cnt = 0;

struct Segment{
	struct P{
		ll l,r,val,maxx;
	}t[(100005) << 2];
	#define l(x) t[x].l
	#define r(x) t[x].r
	#define v(x) t[x].val
	#define ma(x) t[x].maxx
	#define mid ((l + r) >> 1)
	void up(ll now){v(now) = v(l(now)) + v(r(now)),ma(now) = std::max(ma(l(now)),ma(r(now)));}
	ll build(ll l,ll r){
		ll now = ++ cnt;
		if(l == r){
			v(now) = 0;
			ma(now) = 0;
			return now;
		}
		l(now) = build(l,mid);
		r(now) = build(mid + 1,r);
		up(now);
		return now;
	}
	void change(ll now,ll l,ll r,ll p,ll x){
		 if(l == r){
		 v(now) = ma(now) = x;
		 return;	
		 }
		 if(p <= mid)
		 change(l(now),l,mid,p,x);
		 else
		 change(r(now),mid + 1,r,p,x);
		 up(now);
		 return;
	}
	ll q_s(ll now,ll l,ll r,ll nl,ll nr){
		if(nl <= l && r <= nr)
		return v(now);
		ll ans = 0;
		if(nl <= mid)
		ans += q_s(l(now),l,mid,nl,nr);
		if(nr > mid)
		ans += q_s(r(now),mid + 1,r,nl,nr);
		return ans;
	}
	ll q_m(ll now,ll l,ll r,ll nl,ll nr){
		if(nl <= l && r <= nr)
		return ma(now);
		ll ans = 0;
		if(nl <= mid)
		ans = std::max(ans,q_m(l(now),l,mid,nl,nr));
		if(nr > mid)
		ans = std::max(ans,q_m(r(now),mid + 1,r,nl,nr));
		return ans;
	}
	void change_mod(ll now,ll l,ll r,ll nl,ll nr,ll m){
		if(ma(now) < m)
		return;
		if(l == r){
			v(now) %= m;
			ma(now) %= m;
			return ;
		}
		if(nl <= mid)
		change_mod(l(now),l,mid,nl,nr,m);
		if(nr > mid)
		change_mod(r(now),mid + 1,r,nl,nr,m);
		up(now);
	}
}Q;

int main(){
	scanf("%lld%lld",&n,&m);
	ll s = Q.build(1,n);
	for(int i = 1;i <= n;++i){
		ll x;
		scanf("%lld",&x);
		Q.change(1,1,n,i,x);
	}
	for(int i = 1;i <= m;++i){
		ll opt,l,r,x;
		scanf("%lld",&opt);
		if(opt == 1){
			scanf("%lld%lld",&l,&x);
			std::cout<<Q.q_s(1,1,n,l,x)<<std::endl;
		}
		if(opt == 2){
			scanf("%lld%lld%lld",&l,&r,&x);
			if(Q.q_m(1,1,n,l,r) >= x)
			Q.change_mod(1,1,n,l,r,x) ;
		}
		if(opt == 3){
			scanf("%lld%lld",&l,&x);
			Q.change(1,1,n,l,x);
		}
	}
}
posted @ 2021-03-03 20:42  fhq_treap  阅读(72)  评论(0编辑  收藏  举报