zoukankan      html  css  js  c++  java
  • 数据结构刷题

    数据结构刷题记录

    AT1219 歴史の研究 - 莫队

    • 简单的回滚莫队,考虑到维护最大值,加操作好做而减操作难
    • 还可以将每个数可能的贡献算出,即对于一个数x,贡献为x1,x2,...,x*y(y为在整个序列中x出现个数),离散化后,用值域分块维护,只用普通莫队即可
    • 下面是回滚莫队做法的代码
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100010;
    int n, m, len;
    long long a[MAXN], b[MAXN], dr[MAXN];
    int block, bn, B[MAXN];
    long long ans, cm[MAXN], Ans[MAXN], ct[MAXN];
    int s[MAXN], st;
    struct question
    {
    	int l, r, id;
    }ask[MAXN];
    inline bool cmp(const question &a, const question &b)
    {
    	if(B[a.l] ^ B[b.l]) return B[a.l] < B[b.l];
    	return a.r < b.r;
    }
    inline long long max(const long long &x, const long long &y)
    {
    	return x > y ? x : y;
    }
    inline long long min(const long long &x, const long long &y)
    {
    	return x < y ? x : y;
    }
    inline void discrete()
    {
    	len = 0;
    	sort(b + 1, b + n + 1);
    	for (int i = 1; i <= n; i++)
    	{
    		if(i == 1 || b[i] != b[i - 1]) 
    			dr[++len] = b[i]; 
    	}
    }
    inline long long calc(int l, int r)
    {
    	long long res = 0;
    	for (int i = l; i <= r; i++) cm[a[i]] = 0;
    	for (int i = l; i <= r; i++)
    	{
    		cm[a[i]]++;
    		res = max(cm[a[i]] * dr[a[i]], res);
    	}
    	return res;
    }
    int main()
    {
    	scanf("%d %d", &n, &m); block = pow(n, 1.0 / 2.0);
    	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), b[i] = a[i];
    	discrete();
    	for (int i = 1; i <= n; i++) a[i] = lower_bound(dr + 1, dr + len + 1, a[i]) - dr;
    	for (int i = 1; i <= m; i++)
    	{
    		scanf("%d %d", &ask[i].l, &ask[i].r);
    		ask[i].id = i;
    	}
    	for (int i = 1; i <= n; i++) B[i] = (i - 1) / block + 1;
    	sort(ask + 1, ask + m + 1, cmp);
    	bn = B[n];
    	for (int i = 1, j = 1; i <= bn; i++)
    	{
    		int br = min(n, i * block), l = br + 1, r = br;
    		st = 0;//s数组维护出现了那些数
    		ans = 0;
    		for (; B[ask[j].l] == i; j++)
    		{
    			if(B[ask[j].r] == i)
    			{
    				Ans[ask[j].id] = calc(ask[j].l, ask[j].r);
    				continue;
    			}
    			while(r < ask[j].r)
    			{
    				r++;
    				if(!ct[a[r]]) s[++st] = a[r];
    				ct[a[r]]++; ans = max(ans, ct[a[r]] * dr[a[r]]);
    			}
    			long long tmp = ans;
    			while(l > ask[j].l)
    			{
    				l--;
    				ct[a[l]]++; tmp = max(tmp, ct[a[l]] * dr[a[l]]);
    			}
    			Ans[ask[j].id] = tmp;
    			while(l <= br)
    			{
    				ct[a[l]]--;
    				l++;
    			}
    		}
    		for (int i = 1; i <= st; i++) ct[s[i]] = 0;
    	}
    	for (int i = 1; i <= m; i++) printf("%lld
    ", Ans[i]);
    	return 0;
    }
    

    [国家集训队]旅游 - 树剖

    • 简单的树剖题(码量有点大)
    • 考虑点权变为边权后计算的位置即可(记录每条边的位置)
    • 利用线段树懒标记解决区间问题
    • 区间取相反数有点像区间翻转,打个标记就行了
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 5000010;
    int n, m;
    int head[MAXN], nxt[MAXN << 1], v[MAXN << 1], w[MAXN << 1], b[MAXN << 1], cnt;
    int son[MAXN], size[MAXN], d[MAXN], wo[MAXN], fa[MAXN], top[MAXN], sv[MAXN], ff[MAXN], totw;
    int val[MAXN];
    struct SegmentTree
    {
    	int l, r, sum, res, ma, mi;
    	#define l(x) t[x].l
    	#define r(x) t[x].r
    	#define sum(x) t[x].sum
    	#define res(x) t[x].res
    	#define ma(x) t[x].ma
    	#define mi(x) t[x].mi
    }t[MAXN << 2];
    void add(int x, int y, int z, int i)
    {
    	nxt[++cnt] = head[x]; head[x] = cnt; v[cnt] = y; w[cnt] = z; b[cnt] = i;
    }
    void dfs1(int now, int f, int de)
    {
    	size[now] = 1; fa[now] = f; d[now] = de;
    	int maxx = -1;
    	for (int i = head[now]; i; i = nxt[i])
    	{
    		if(v[i] == f) continue;
    		dfs1(v[i], now, de + 1);
    		if(size[v[i]] > maxx) maxx = size[v[i]], son[now] = v[i], sv[now] = i;
    		size[now] += size[v[i]];
    	}
    }
    void dfs2(int now, int topf, int p)
    {
    	top[now] = topf; ff[now] = p;
    	if(now ^ 1) wo[b[p]] = ++totw, val[totw] = w[p];
    	if(!son[now]) return;
    	dfs2(son[now], topf, sv[now]);
    	for (int i = head[now]; i; i = nxt[i])
    	{
    		if(v[i] == fa[now] || v[i] == son[now]) continue;
    		dfs2(v[i], v[i], i);
    	}
    }
    void maintain(int p)
    {
    	sum(p) = sum(p << 1) + sum(p << 1 | 1);
    	ma(p) = max(ma(p << 1), ma(p << 1 | 1));
    	mi(p) = min(mi(p << 1), mi(p << 1 | 1));
    }
    void build(int p, int l, int r)
    {
    	l(p) = l; r(p) = r;
    	if(l == r) 
    	{
    	 	sum(p) = mi(p) = ma(p) = val[r];
    	 	res(p) = 0;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(p << 1, l, mid);
    	build(p << 1 | 1, mid + 1, r);
    	maintain(p);
    }
    void pushdown(int p)
    {
    	if(res(p))
    	{
    		sum(p << 1) = -sum(p << 1);
    		sum(p << 1 | 1) = -sum(p << 1 | 1);
    		swap(mi(p << 1), ma(p << 1));
    		mi(p << 1) = -mi(p << 1);
    		ma(p << 1) = -ma(p << 1);
    		swap(mi(p << 1 | 1), ma(p << 1 | 1));
    		mi(p << 1 | 1) = -mi(p << 1 | 1);
    		ma(p << 1 | 1) = -ma(p << 1 | 1);
    		res(p) ^= 1;
    		res(p << 1) ^= 1;
    		res(p << 1 | 1) ^= 1;
    	}
    }
    int askma(int p, int l, int r)
    {
    	if(l(p) >= l && r(p) <= r) return ma(p);
    	pushdown(p);
    	int ans = -100000000;
    	int mid = (l(p) + r(p)) >> 1;
    	if(l <= mid) ans = max(ans, askma(p << 1, l, r));
    	if(r > mid) ans = max(ans, askma(p << 1 | 1, l, r));
    	return ans;
    }
    int asksu(int p, int l, int r)
    {
    	if(l(p) >= l && r(p) <= r) return sum(p);
    	pushdown(p);
    	int ans = 0;
    	int mid = (l(p) + r(p)) >> 1;
    	if(l <= mid) ans += asksu(p << 1, l, r);
    	if(r > mid) ans += asksu(p << 1 | 1, l ,r);
    	return ans;
    }
    int askmi(int p, int l, int r)
    {
    	if(l(p) >= l && r(p) <= r)
    	{
    		return mi(p);
    	}
    	pushdown(p);
    	int ans = 100000000;
    	int mid = (l(p) + r(p)) >> 1;
    	if(l <= mid) ans = min(ans, askmi(p << 1, l, r));
    	if(r > mid) ans = min(ans, askmi(p << 1 | 1, l, r));
    	return ans;
    }
    void modify(int p, int l, int r)
    {
    	if(l(p) >= l && r(p) <= r)
    	{
    		res(p) ^= 1;
    		sum(p) = -sum(p);
    		int tmp = ma(p);
    		ma(p) = -mi(p);
    		mi(p) = -tmp;
    		return;
    	}
    	pushdown(p);
    	int mid = (l(p) + r(p)) >> 1;
    	if(l <= mid) modify(p << 1, l, r);
    	if(r > mid) modify(p << 1 | 1, l, r);
    	maintain(p);
    }
    void change(int p, int x, int dt)
    {
    	if(l(p) == r(p))
    	{
    		sum(p) = ma(p) = mi(p) = dt;
    		return;
    	}
    	pushdown(p);
    	int mid = (l(p) + r(p)) >> 1;
    	if(x <= mid) change(p << 1, x, dt);
    	else change(p << 1 | 1, x, dt);
    	maintain(p);
    }
    void mo(int x, int y)
    {
    	while(top[x] != top[y])
    	{
    		if(d[top[x]] < d[top[y]]) swap(x, y);
    		modify(1, wo[b[ff[top[x]]]], wo[b[ff[x]]]);
    		x = fa[top[x]];
    	}
    	if(x == y) return;
    	if(d[x] > d[y]) swap(x, y);
    	modify(1, wo[b[sv[x]]], wo[b[ff[y]]]);
    	return;
    }
    int mam(int x, int y)
    {
    	int ans = -100000000;
    	while(top[x] != top[y])
    	{
    		if(d[top[x]] < d[top[y]]) swap(x, y);
    		ans = max(ans, askma(1, wo[b[ff[top[x]]]], wo[b[ff[x]]]));
    		x = fa[top[x]];
    	}
    	if(x == y) return ans;
    	if(d[x] > d[y]) swap(x, y);
    	ans = max(ans, askma(1, wo[b[sv[x]]], wo[b[ff[y]]]));
    	return ans;
    }
    int mim(int x, int y)
    {
    	int ans = 100000000;
    	while(top[x] != top[y])
    	{
    		if(d[top[x]] < d[top[y]]) swap(x, y);
    		ans = min(ans, askmi(1, wo[b[ff[top[x]]]], wo[b[ff[x]]]));
    		x = fa[top[x]];
    	}
    	if(x == y) return ans;
    	if(d[x] > d[y]) swap(x, y);
    	ans = min(ans, askmi(1, wo[b[sv[x]]], wo[b[ff[y]]]));
    	return ans;
    }
    int sus(int x, int y)
    {
    	int ans = 0;
    	while(top[x] != top[y])
    	{
    		if(d[top[x]] < d[top[y]]) swap(x, y);
    		ans += asksu(1, wo[b[ff[top[x]]]], wo[b[ff[x]]]);
    		x = fa[top[x]];
    	}
    	if(x == y) return ans;
    	if(d[x] > d[y]) swap(x, y);
    	ans += asksu(1, wo[b[sv[x]]], wo[b[ff[y]]]);
    	return ans;	
    }
    int main()
    {
    //	freopen("inp", "r", stdin);
    //	freopen("out", "w", stdout);
    	scanf("%d", &n);
    	for (int i = 1; i < n; i++)
    	{
    		int x, y, z;
    		scanf("%d %d %d", &x, &y, &z); x++, y++;
    		add(x, y, z, i);
    		add(y, x, z, i);
    	}
    	dfs1(1, 0, 1);
    	dfs2(1, 1, 1);
    	build(1, 1, totw);
    	scanf("%d", &m);
    	for (int i = 1; i <= m; i++)
    	{
    		char opt[10];
    		scanf("%s", opt + 1);
    		if(opt[1] == 'C')
    		{
    			int x, y;
    			scanf("%d %d", &x, &y);
    			change(1, wo[x], y);
    		}
    		else if(opt[1] == 'N')
    		{
    			int x, y;
    			scanf("%d %d", &x, &y);x++, y++;
    			mo(x, y);
    		}
    		else if(opt[1] == 'S')
    		{
    			int x, y;
    			scanf("%d %d", &x, &y);x++, y++;
    			printf("%d
    ", sus(x, y));
    		}
    		else if(opt[2] == 'A')
    		{
    			int x, y;
    			scanf("%d %d", &x, &y);x++, y++;
    			printf("%d
    ", mam(x, y));
    		}	
    		else 
    		{
    			int x, y;
    			scanf("%d %d", &x, &y);x++, y++;
    			printf("%d
    ", mim(x, y));
    		}
    	}
    	return 0;
    }
    

    [NOI Online #1 提高组]冒泡排序 - 树状数组、逆序对

    • 比较有思维的一道题
    • 首先考虑冒泡排序的本质是通过n轮检查邻项是否逆序,并通过邻项的交换减少逆序对个数从而使序列有序
    • 我们用(fm[i])表示第i个数前面有几个数比他大,总逆序对个数为(sum_{i=1}^{n}{fm[i]})
    • 考虑一轮冒泡排序,每一位置的(c[i])必定会减一,且仅会减一
    • 故k轮后(Ans = sum_{i=1}^{n}{[fm[i]geq{k + 1}] imes(fm[i] - k)})
    • 我们求出了fm数组后,用两个树状数组在fm[i]值域上分别维护前缀个数和前缀(sum{fm[i]})
    • 注意当k >= n 时特判逆序对个数必定为0(n-1轮就有序了)
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 5000010;
    int n, m, a[MAXN];
    long long c[MAXN], f[MAXN];
    int fm[MAXN];
    inline int lowbit(int x) { return x & (-x); }
    inline long long ask(int x)
    {
    	long long ans = 0;
    	while(x)
    	{
    		ans += c[x];
    		x -= lowbit(x);
    	}
    	return ans;
    }
    inline void add(int x, int dt)
    {
    	if(x == 0) return;
    	while(x <= n)
    	{
    		c[x] += dt;
    		x += lowbit(x);
    	}
    }
    inline long long ask2(int x)
    {
    	long long ans = 0;
    	while(x)
    	{
    		ans += f[x];
    		x -= lowbit(x);
    	}
    	return ans;
    }
    inline void add2(int x, int dt)
    {
    	if(x == 0) return;
    	while(x <= n)
    	{
    		f[x] += dt;
    		x += lowbit(x);
    	}
    }
    int main()
    {	
    	scanf("%d %d", &n, &m);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	for (int i = 1; i <= n; i++)
    	{
    		fm[i] = i - 1 - ask(a[i]);
    		add(a[i], 1);
    	}
    	for (int i = 1; i <= n; i++) c[i] = 0;
    	for (int i = 1; i <= n; i++)
    	{
    		add(fm[i], 1);
    		add2(fm[i], fm[i]);
    	}
    	for (int i = 1; i <= m; i++)
    	{
    		int opt, x;
    		scanf("%d %d", &opt, &x);
    		if(opt == 1)
    		{
    			add(fm[x], -1); add2(fm[x], -fm[x]);
    			add(fm[x + 1], -1); add2(fm[x + 1], -fm[x + 1]);
    			if(a[x] > a[x + 1]) fm[x + 1]--;
    			else fm[x]++;
    			swap(fm[x], fm[x + 1]);
    			swap(a[x], a[x + 1]);
    			add(fm[x], 1); add2(fm[x], fm[x]);
    			add(fm[x + 1], 1); add2(fm[x + 1], fm[x + 1]);
    		}
    		else 
    		{
    			if(x >= n) printf("0
    ");
    			else
    			{
    				long long si = ask2(n) - ask2(x), ti = ask(n) - ask(x);
    				printf("%lld
    ", si - ti * x);
    			}
    		}
    	}
    	return 0;
    }
    

    [HAOI2007]理想的正方形 - 单调队列

    • 考虑我们如果可以求出每一个位置((i, j))为右下角的(n imes{n})的矩形中的最大值和最小值,即可求出答案
    • 两次单调队列先做一行,再在每一列上根据第一次做的答案,求出矩形上的最值
    • 即一次做到点到线上的维护,第二次做到线到面上的维护
    #include <bits/stdc++.h>
    using namespace std;
    const int SIZE = 1010;
    int a, b, n;
    int g[SIZE][SIZE];
    int ma[SIZE][SIZE], mi[SIZE][SIZE], maa[SIZE][SIZE], mii[SIZE][SIZE];
    int q[SIZE], l = 1, r = 0;
    int main()
    {
    	scanf("%d %d %d", &a, &b, &n);
    	for (int i = 1; i <= a; i++)
    		for (int j = 1; j <= b; j++)
    			scanf("%d", &g[i][j]);
    	for (int i = 1; i <= a; i++)
    	{
    		l = 1; r = 0;
    		for (int j = 1; j <= b; j++)
    		{
    			while(l <= r && j - q[l] + 1 > n) l++;
    			while(l <= r && g[i][j] >= g[i][q[r]]) r--;
    			q[++r] = j;
    			ma[i][j] = g[i][q[l]];
    		}
    	}
    	/*
    	for (int i = 1; i <= a; i++)
    	{
    		for (int j = 1; j <= b; j++)
    			cout << ma[i][j] << " ";
    		cout << endl;
    	}
    	*/
    	for (int i = 1; i <= a; i++)
    	{
    		l = 1; r = 0;
    		for (int j = 1; j <= b; j++)
    		{
    			while(l <= r && j - q[l] + 1 > n) l++;
    			while(l <= r && g[i][j] <= g[i][q[r]]) r--;
    			q[++r] = j;
    			mi[i][j] = g[i][q[l]];
    		}
    	}
    	for (int i = 1; i <= b; i++)
    	{
    		l = 1; r = 0; 
    		for (int j = 1; j <= a; j++)
    		{
    			while(l <= r && j - q[l] + 1 > n) l++;
    			while(l <= r && ma[j][i] >= ma[q[r]][i]) r--;
    			q[++r] = j;
    			maa[j][i] = ma[q[l]][i];
    		}
    	}
    	for (int i = 1; i <= b; i++)
    	{
    		l = 1; r = 0; 
    		for (int j = 1; j <= a; j++)
    		{
    			while(l <= r && j - q[l] + 1 > n) l++;
    			while(l <= r && mi[j][i] <= mi[q[r]][i]) r--;
    			q[++r] = j;
    			mii[j][i] = mi[q[l]][i];
    		}
    	}
    	int ans = 1000000000;
    	for (int i = n; i <= a; i++)
    		for (int j = n; j <= b; j++)
    		{
    			ans = min(ans, maa[i][j] - mii[i][j]);
    		}
    	printf("%d", ans);
    	return 0;
    }
    

    [HEOI2012]采花

    • 首先(10^5)的数据可以用莫队做
    • 更高效的做法是用树状数组做,与HH的项链相似
    • 考虑先预处理出每个位置的下一个相同颜色的位置,同时在每个颜色第二次出现的位置先加上一
    • 将询问离线做,按左端点排序,则每次递增删除当前未在左区间的点的贡献,即将此位置的下个同颜色的位置贡献减去,下下个位置贡献加上(此中颜色当前最左边位置少了一个)
    • 再用树状数组计算前缀和,作差即可
    • (Ans = sum(r) - sum(l - 1))
    /*
    #include <bits/stdc++.h>
    using namespace std;
    int read() 
    {
    	int a = 0, f = 1; char c = getchar();
    	while(c > '9' || c < '0') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') {a = a * 10 + c - '0'; c = getchar();}
    	return a * f;
    }
    const int MAXN = 2e6 + 14;
    int n, m, t, xl, xr, block;
    int ans;
    int a[MAXN];
    int cnt[MAXN];
    int Ans[MAXN];
    struct question 
    {
    	int l, r, bo, id;
    	#define l(x) ask[x].l
    	#define r(x) ask[x].r
    	#define bo(x) ask[x].bo
    	#define id(x) ask[x].id
    } ask[MAXN];
    bool cmp(question a, question b) 
    {
    	return a.bo ^ b.bo ? a.bo < b.bo : a.bo % 2 == 1 ? a.r < b.r : a.r > b.r;
    }
    void add(int x) 
    {
    	cnt[a[x]]++;
    	if(cnt[a[x]] == 2) ans++;
    }
    void del(int x) 
    {
    	cnt[a[x]]--;
    	if(cnt[a[x]] == 1) ans--;
    }
    int main() {
    	n = read(); t = read(); m = read();  block = sqrt(n);
    	for (int i = 1; i <= n; i++) a[i] = read();
    	for (int i = 1; i <= m; i++) l(i) = read(), r(i) = read(), bo(i) = l(i) / block, id(i) = i;
    	sort(ask + 1, ask + m + 1, cmp);
    	for (int i = 1; i <= m; i++) 
    	{
    		while(xr > r(i)) del(xr--);
    		while(xr < r(i)) add(++xr);
    		while(xl < l(i)) del(xl++);
    		while(xl > l(i)) add(--xl);
    		Ans[id(i)] = ans;
    	}
    	for (int i = 1; i <= m; i++) 
    	{  
    		printf("%d
    ", Ans[i]);
    	}
    	return 0;
    }
    */
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 2000010;
    inline int read()
    {
    	int a = 0, f = 1; char c = getchar();
    	while(c > '9' || c < '0') { if(c == '-') f = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') { a = a * 10 + c - '0'; c = getchar(); }
    	return a * f;
    }
    int n, m, t;
    int a[MAXN], pre[MAXN], Ans[MAXN], la[MAXN], cnt[MAXN];
    struct node
    {
    	int l, r, id;
    }ask[MAXN];
    inline bool cmp(const node &a, const node &b)
    {
    	return a.l < b.l;
    }
    int c[MAXN];
    inline int lowbit(int x)
    {
    	return x & (-x);
    }
    inline void add(int x, int dt)
    {
    	while(x <= n)
    	{
    		c[x] += dt;
    		x += lowbit(x);
    	}
    }
    inline int sum(int x)
    {
    	int ans = 0;
    	while(x)
    	{
    		ans += c[x];
    		x -= lowbit(x);
    	}
    	return ans;
    }
    int main()
    {
    	n = read(); t = read(); m = read(); 
    	for (int i = 1; i <= n; i++) a[i] = read();
    	for (int i = 1; i <= m; i++) ask[i].l = read(), ask[i].r = read(), ask[i].id = i;
    	for (int i = n; i >= 1; i--)
    	{
    		if(pre[a[i]]) la[i] = pre[a[i]];
    		pre[a[i]] = i;
    	}
    	for (int i = 1; i <= t; i++)
    		if(la[pre[i]]) 
    			add(la[pre[i]], 1);
    	sort(ask + 1, ask + m + 1, cmp);	
    	int nl = 1;
    	for (int i = 1; i <= m; i++)
    	{
    		while(nl < ask[i].l)
    		{
    			if(la[nl]) add(la[nl], -1);
    			if(la[la[nl]]) add(la[la[nl]], 1);
    			nl++;
    		}
    		Ans[ask[i].id] = sum(ask[i].r) - sum(ask[i].l - 1);
    	}
    	for (int i = 1; i <= m; i++)
    		printf("%d
    ", Ans[i]);
    	return 0;
    }
    

  • 相关阅读:
    .NET写的Email可以群发邮件的实用函数
    動網中用到的幾個Function和一個JS[base64encode,base64decode,md5,sendmail,js]
    HTML在线编辑器
    IIS虚拟目录控制类
    实用正则表达式(实用篇)
    IIS站点管理类
    精巧sql语句
    圖片滾動代碼
    c# 添加图片水印,可以指定水印位置+生成缩略图
    JavaScript旋转图片
  • 原文地址:https://www.cnblogs.com/hangzz/p/13258840.html
Copyright © 2011-2022 走看看