SEERC2018 - Gym101964 【10/11】

题目链接

A - Numbers

不会。

B - Broken Watch

考虑固定1号针,枚举2号针,计算3号针放置的方案数。

显然,3号针应在前面两个针的反向延长线围成的扇形区域内,这样可以放置的方案数就是1+2+...+n/2种。此外当n为偶数且前两个针反向时,显然3号针可以放在剩余的任意位置上,即偶数的答案需要加上n-2

枚举1号针的位置,答案要乘上n

然后考虑针的顺序问题,三个长度都相同的时候除以3!,有两根相同的时候除以2!

由于对2^{64}取模直接用unsigned long long自然溢出,这样除法用比较难用逆元处理,在除6操作的时候需要讨论一下运算顺序保证精度。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

ull n, ans;
int a, b, c;

int main()
{
	cin >> a >> b >> c >> n;
	ull hf = n >> 1;
	if(n & 1) ans = hf*(hf+1);
	else ans = ((hf+1)*hf/2 - 1)*2 + n - 2;
	if(a == b && b == c)
	{
		ans /= 2;
		if(ans % 3 == 0) ans = ans/3*n;
		else ans = n/3*ans;
	}
	else if(a == b || a == c || b == c)
	{
		ans /= 2;
		ans *= n;
	}
	else ans *= n;
	cout << ans << endl;
	return 0;
}

C - Tree

考虑构造一棵只由黑色点构成的树,边的权值就是原树中的距离。这样黑色点中两两最长的距离就是这棵树的直径。

首先Floyd预处理原树中点之间的距离,然后枚举新树的直径。假设新树的直径是点对\left (s,t \right )之间的距离,然后枚举剩余的黑点。黑点k能被加入到新树中的条件显然是不能使直径增加,也就是dis[s][k]\leq dis[s][t]dis[k][t]\leq dis[s][t]

总的时间复杂度O(n^{3})

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = 105;
const int INF = 0x3f3f3f3f;

int n, m, x, y;
int dis[maxn][maxn];
int isBlack[maxn];

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++) dis[i][j] = INF;
    for(int i = 1;i <= n;i++)
        scanf("%d", &isBlack[i]), dis[i][i] = 0;
    for(int i = 1;i < n;i++)
        scanf("%d%d", &x, &y), dis[x][y] = dis[y][x] = 1;
    for(int k = 1;k <= n;k++)
    {
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= n;j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
        }
    }
    int ans = INF;
    for(int i = 1;i <= n;i++)
    {
        for(int j = i;j <= n;j++)
        {
            if(!isBlack[j] || !isBlack[i]) continue;
            int cnt = 0;
            for(int k = 1;k <= n;k++)
            {
                if(isBlack[k] && dis[i][k] <= dis[i][j] && dis[k][j] <= dis[i][j])
                    cnt++;
            }
            if(cnt >= m) ans = min(ans, dis[i][j]);
        }
    }
    printf("%d\n", ans);
    return 0;
}

D - Space Station

考虑如果某个子树中的技能起止点一共有x个,那么当x为奇数的时候,这个子树的根与其父亲之间的边只需要走一次,否则就需要走两次。

那么令dp[i][j]为按照题目要求遍历完以i为根的子树,且子树内有j个技能起始点的最小距离(不含技能时间),那么有:

dp[u][i+j] = min\left \{ dp[u][i]+dp[v][j]+w \right \} \left (j & 1)

dp[u][i+j] = min\left \{ dp[u][i]+dp[v][j]+2*w \right \} \left (! j & 1)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;

const int maxn = 1050;
const int INF = 0x3f3f3f3f;

int t, n, m, val, u, v, w;
int no, head[maxn], sz[maxn];
int dp[maxn][maxn << 1], f[maxn];
struct node
{
	int to, nxt;
	int w;
}e[maxn << 1];

void add(int a, int b, int w)
{
	e[no].to = b, e[no].nxt = head[a], e[no].w = w;
	head[a] = no++;
	e[no].to = a, e[no].nxt = head[b], e[no].w = w;
	head[b] = no++;
}

void init()
{
	no = 0;
	memset(sz, 0, sizeof(sz));
	memset(head, -1, sizeof(head));
}

void dfs(int u, int fa)
{
	sz[u] = 1;
	memset(dp[u], 0, sizeof(dp[u]));
	for(int i = head[u];i != -1;i = e[i].nxt)
	{
		int v = e[i].to;
		if(v == fa) continue;
		dfs(v, u);
		for(int j = 0;j <= m*2;j++) f[j] = INF;
		for(int j = 0;j <= sz[u];j++)
		{
			for(int k = 0;k <= sz[v];k++)
			{
				if(k & 1) f[j+k] = min(f[j+k], dp[u][j] + dp[v][k] + e[i].w);
				else f[j+k] = min(f[j+k], dp[u][j] + dp[v][k] + 2*e[i].w);
			}
		}
		sz[u] += sz[v];
		for(int j = 0;j <= m*2;j++) dp[u][j] = f[j];
	}
}

int main()
{
	scanf("%d", &t);
	while(t--)
	{
		init();
		scanf("%d%d%d", &n, &m, &val);
		for(int i = 1;i < n;i++)
		{
			scanf("%d%d%d", &u, &v, &w);
			add(u, v, w);
		}
		dfs(1, 0);
		int ans = INF;
		for(int i = 0;i <= m*2;i += 2)
			ans = min(ans, dp[1][i] + i/2*val);
		printf("%d\n", ans);
	}
	return 0;
}

E - Fishermen

考虑位于坐标(x, y)的鱼,如果y > L,那么肯定不能被捕捉到。

否则能够捕捉到这条鱼的渔夫应位于[x - (L - y), x + (L - y)]内。

将渔夫按照x升序排序,对于每一条鱼找出能被捕捉到的渔夫区间然后进行一个区间加的操作。最后对所有渔夫单点查询即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <cstring>
using namespace std;
typedef long long ll;

const int maxn = 2e5 + 10;

int n, m, l, ans[maxn], pos[maxn];
int tree[maxn << 2], lazy[maxn << 2];
pair<int, int> Fish[maxn];
struct Man
{
    int x, id;
    bool operator < (const Man o) const {return x < o.x;}
}p[maxn];

void push_down(int rt, int l, int r)
{
    if(lazy[rt] != 0)
    {
        lazy[rt<<1] += lazy[rt], lazy[rt<<1|1] += lazy[rt];
        tree[rt<<1] += lazy[rt], tree[rt<<1|1] += lazy[rt];
        lazy[rt] = 0;
    }
}

void update(int rt, int l, int r, int aiml, int aimr)
{
    if(l > r || r < aiml || l > aimr) return;
    if(l >= aiml && r <= aimr)
    {
        tree[rt]++, lazy[rt]++;
        return;
    }
    push_down(rt, l, r);
    int mid = (l + r) >> 1;
    update(rt<<1, l, mid, aiml, aimr);
    update(rt<<1|1, mid+1, r, aiml, aimr);
    tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}

void query(int rt, int l, int r)
{
    if(l == r) {ans[p[l].id] = tree[rt]; return;}
    push_down(rt, l, r);
    int mid = (l + r) >> 1;
    query(rt<<1, l, mid), query(rt<<1|1, mid+1, r);
}

int main()
{
    scanf("%d%d%d", &n, &m, &l);
    for(int i = 0;i < n;i++)
        scanf("%d%d", &Fish[i].first, &Fish[i].second);
    for(int i = 1;i <= m;i++)
    {
        scanf("%d", &p[i].x);
        p[i].id = i, pos[i] = p[i].x;
    }
    sort(p+1, p+m+1), sort(pos+1, pos+m+1);
    memset(tree, 0, sizeof(tree));
    memset(lazy, 0, sizeof(lazy));
    for(int i = 0;i < n;i++)
    {
        if(Fish[i].second > l) continue;
        int len = l - Fish[i].second;
        int L = lower_bound(pos+1, pos+m+1, Fish[i].first - len) - pos;
        int R = upper_bound(pos+1, pos+m+1, Fish[i].first + len) - pos - 1;
        update(1, 1, m, L, R);
    }
    query(1, 1, m);
    for(int i = 1;i <= m;i++)
        printf("%d\n", ans[i]);
    return 0;
}

F - Min Max Convert

考虑将一个区间的数全部变成某一端点的数,最多需要两步, 例如:

如果a[L]>a[L+1],则先对[L+1, R]min,再对[L, R]max。反之亦然。右端点同理。

考虑如果将一个区间的数全部变成某个数,那么这个数必然在原序列里出现。

那么b中的每一个元素必然在a中可以找到一个匹配的位置,且这个位置序列是非递减的。

此时就可以分成若干个子问题:若b[i]b[j]全部匹配了a[k],那么就需要将[min(i,j,k), max(i,j,k)]变为这个值。具体的操作只需分类讨论即可,可以发现总数目不会超过n*2

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = 1e5 + 10;

int n, a[maxn], b[maxn], pos[maxn];
char ans[maxn<<1];
int no, tot, L[maxn<<1], R[maxn<<1];
struct node
{
	int x, l, r;
}e[maxn];

void add(char c, int l, int r)
{
	ans[no] = c, L[no] = l, R[no] = r;
	no++;
}

int main()
{
	scanf("%d", &n);
	for(int i = 1;i <= n;i++) scanf("%d", &a[i]);
	for(int i = 1;i <= n;i++) scanf("%d", &b[i]);
	int now = 1;
	for(int i = 1;i <= n;i++)
	{
		while(now <= n && a[now] != b[i]) now++;
		if(now > n) {puts("-1"); return 0;}
		pos[i] = now;
	}
	now = 1, tot = no = 0;
	for(int i = 1;i <= n;i++)
	{
		if(pos[i] == pos[i+1]) continue;
		e[tot++] = (node){pos[i], now, i};
		now = i + 1;
	}
	for(int i = tot-1;i >= 0;i--)
	{
		if(e[i].x >= e[i].l) continue;
		if(a[e[i].x] >= a[e[i].x+1])
		{
			add('m', e[i].x+1, e[i].r);
			add('M', e[i].x, e[i].r);
		}
		else
		{
			add('M', e[i].x+1, e[i].r);
			add('m', e[i].x, e[i].r);
		}
	}
	for(int i = 0;i < tot;i++)
	{
		if(e[i].x <= e[i].r) continue;
		if(a[e[i].x] <= a[e[i].x-1])
		{
			add('M', e[i].l, e[i].x-1);
			add('m', e[i].l, e[i].x);
		}
		else
		{
			add('m', e[i].l, e[i].x-1);
			add('M', e[i].l, e[i].x);
		}
	}
	for(int i = 0;i < tot;i++)
	{
		if(e[i].l > e[i].x || e[i].r < e[i].x) continue;
		if(e[i].x > e[i].l)
		{
			if(a[e[i].x] >= a[e[i].x-1])
			{
				add('m', e[i].l, e[i].x-1);
				add('M', e[i].l, e[i].x);
			}
			else
			{
				add('M', e[i].l, e[i].x-1);
				add('m', e[i].l, e[i].x);
			}
		}
		if(e[i].x < e[i].r)
		{
			if(a[e[i].x] >= a[e[i].x+1])
			{
				add('m', e[i].x+1, e[i].r);
				add('M', e[i].x, e[i].r);
			}
			else
			{
				add('M', e[i].x+1, e[i].r);
				add('m', e[i].x, e[i].r);
			}
		}
	}
	printf("%d\n", no);
	for(int i = 0;i < no;i++)
		printf("%c %d %d\n", ans[i], L[i], R[i]);
	return 0;
}

G - Matrix Queries

根据矩阵贡献是四个子矩阵贡献之和+1这一条件,猜想结论:如果一个矩阵的所有子矩阵中,有两种颜色(不纯)的子矩阵的数目是x,那么这个矩阵的贡献就是4*x+1

下面用数学归纳法证明这个结论。

对于k=0,显然不纯的矩阵数为0且贡献为1,结论成立。

假设k=p时结论成立,此时设k=p+1阶矩阵的四个p阶子矩阵中不纯的子矩阵数目分别为x1, x2, x3, x4,显然总的数目为x1+x2+x3+x4+1。每个子矩阵的贡献分别为4*x1, 4*x2, 4*x3, 4*x4,总的贡献为4*(x1+x2+x3+x4)+54*(x1+x2+x3+x4+1)+1。即k=p+1时结论也成立。

综上此结论成立。

现在要统计不纯的子矩阵数目,用所有矩阵数目减去纯的矩阵数目。考虑如果存在k阶纯的矩阵,显然要有连续2^{k}行和2^{k}列被改变的数目的奇偶性相同。于是就只需要统计有多少2^{k}连续的行与列改变了相同奇偶性的次数,二者的乘积就是k阶纯的矩阵的数目。这个操作可以用线段树来实现。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = (1 << 20) + 5;

int n, m, sz, op, x;
ll tree[2][maxn << 2], ans[2][21];

void update(int op, int rt, int l, int r, int deep, int aim)
{
	if(l == r) {tree[op][rt] ^= 1; return;}
	int mid = (l + r) >> 1;
	if(aim <= mid) update(op, rt<<1, l, mid, deep+1, aim);
	else update(op, rt<<1|1, mid+1, r, deep+1, aim);
	if(tree[op][rt] != -1) ans[op][deep]--;
	if(tree[op][rt<<1] == tree[op][rt<<1|1]) tree[op][rt] = tree[op][rt<<1];
	else tree[op][rt] = -1;
	if(tree[op][rt] != -1) ans[op][deep]++;
}

int main()
{
	scanf("%d%d", &sz, &m);
	n = 1 << sz;
	memset(tree, 0, sizeof(tree));
	ll sum = 0;
	for(int i = 1;i <= sz;i++)
	{
		ans[0][i] = ans[1][i] = 1 << (i-1);
		sum += (1LL << (i*2-2));
	}
	for(int i = 1;i <= m;i++)
	{
		scanf("%d%d", &op, &x);
		update(op, 1, 1, n, 1, x);
		ll now = 0;
		for(int j = 1;j <= sz;j++)
			now += ans[0][j]*ans[1][j];
		ll ans = 4*(sum - now) + 1;
		printf("%I64d\n", ans);
	}
	return 0;
}

H - Modern Djinn

正经解法不会。毕竟这题有spj,写了个随机给莽过去了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = 200050;

int t, n, m;
int u[maxn], v[maxn], flag[maxn];

int main()
{
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d%d", &n, &m);
		for(int i = 1;i <= m;i++)
			scanf("%d%d", &u[i], &v[i]);
		int num = m/4 + 1, now = 0;
		while(now < num)
		{
			 now = 0;
			 for(int i = 1;i <= n;i++)
				 flag[i] = rand() % 2;
			 for(int i = 1;i <= m;i++)
			 {
				 if(flag[u[i]] && !flag[v[i]])
					 now++;
			 }
		}
		printf("%d\n", now);
		int cnt = 0;
		for(int i = 1;i <= m;i++)
		{
			if(flag[u[i]] && !flag[v[i]])
			{
				printf("%d", i);
				cnt++;
				if(cnt == now) printf("\n");
				else printf(" ");
			}
		}
	}
	return 0;
}

I - Inversion

根据逆序对的性质首先可以还原出原序列。

根据定义,一个合法的序列必然是单调递增,且最小值的左侧都比它大,最大值的右侧都比它小。

考虑dp[i]表示以i为序列的最后一个点,合法的方案数。dp[i] = Sum(dp[j])j < i且 a[j] < a[i] 且 不存在j<k<i满足a[j]<a[k]<a[i]

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = 105;

int n, m, u, v, a[maxn], in[maxn];
ll dp[maxn];
bool vis[maxn];

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1;i <= m;i++)
	{
		scanf("%d%d", &u, &v);
		if(u < v) swap(u, v);
		in[v]++;
	}
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= n;j++)
		{
			if(vis[j]) continue;
			if(in[i]) in[i]--;
			else {a[i] = j, vis[j] = 1; break;}
		}
	}
	for(int i = 1;i <= n;i++)
	{
		ll res = 0, maxx = 0;
		for(int j = i-1;j >= 1;j--)
		{
			if(a[j] > a[i]) continue;
			if(a[j] > maxx) res += dp[j], maxx = a[j];
		}
		dp[i] = max(1LL, res);
	}
	ll ans = 0, maxx = 0;
	for(int i = n;i >= 1;i--)
	{
		if(a[i] > maxx)
			ans += dp[i], maxx = a[i];
	}
	printf("%I64d\n", ans);
	return 0;
}

J - Rabbit vs Turtle

首先反向dijkstra处理出兔子从每个点到终点的最短路长度,然后线性遍历一遍兔子路径上的点,依次判断此处作弊是否合法即可,时间复杂度O(mlogm+n)

不是很懂现场赛为什么过的这么少……大概歪榜了吧(滑稽

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = 200050;
const int maxm = 400050;
const ll INF = 1LL << 60;

int n, m, u, v, t, r, pr, pt, vis[maxn];
int tPath[maxn], tSleep[maxn], rPath[maxn];
ll dis[maxn], sum[maxn];
vector<int> G[maxn], W[maxn], ans;
struct node
{
	int u, v, t, r;
}e[maxm];
struct point
{
	int x;
	bool operator < (const point &o) const
	{
		return dis[x] > dis[o.x];
	}
};

void dijkstra(int s)
{
	memset(vis, 0, sizeof(vis));
	for(int i = 1;i <= n;i++) dis[i] = INF;
	priority_queue<point> que;
	dis[s] = 0, vis[s] = 1;
	que.push((point){s});
	while(!que.empty())
	{
		point now = que.top();
		que.pop();
		int u = now.x;
		vis[u] = 0;
		for(int i = 0;i < G[u].size();i++)
		{
			int v = G[u][i];
			if(dis[v] > dis[u] + W[u][i])
			{
				dis[v] = dis[u] + W[u][i];
				if(!vis[v]) {vis[v] = 1; que.push((point){v});}
			}
		}
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1;i <= m;i++)
	{
		scanf("%d%d%d%d", &u, &v, &t, &r);
		G[v].push_back(u);
		W[v].push_back(r);
		e[i] = (node){u, v, t, r};
	}
	scanf("%d", &pt);
	for(int i = 1;i <= pt;i++)
		scanf("%d%d", &tPath[i], &tSleep[i]);
	scanf("%d", &pr);
	for(int i = 1;i <= pr;i++) scanf("%d", &rPath[i]);
	dijkstra(n);
	for(int i = pt;i >= 1;i--) sum[i] = sum[i+1] + e[tPath[i]].t;
	int now = 1;
	ll tCnt = 0, rCnt = 0;
	for(int i = 1;i <= pr;i++)
	{
		int tp = rPath[i];
		if(dis[e[tp].u] == dis[e[tp].v] + e[tp].r)
		{
			rCnt += e[tp].r;
			continue;
		}
		while(now <= pt && tCnt + tSleep[now-1] + e[tPath[now]].t <= rCnt)
		{
			tCnt += tSleep[now-1] + e[tPath[now]].t;
			now++;
		}
		if(tCnt + tSleep[now-1] + sum[now] >= rCnt + dis[e[tp].u])
			ans.push_back(e[tp].u);
		rCnt += e[tp].r;
	}
	sort(ans.begin(), ans.end());
	printf("%d\n", ans.size());
	for(int i = 0;i < (int)ans.size();i++)
		printf("%d ", ans[i]);
	printf("\n");
	return 0;
}

K - Points and Rectangles

学习了一下大佬的博客orz,这种用二维线段树之类的空间会爆炸的问题基本上都可以用CDQ分治来解决。

CDQ分治和普通分治的区别就在于CDQ分治的左儿子的答案会对右儿子的答案产生影响,实际上归并排序就是一个标准的CDQ分治。

将点看作一个元素,矩形的左右边分别看作一个元素,然后在从左往右扫的时候,矩形的边和点之间相互会有贡献。比如在矩形之前加的点对矩形的左边有正贡献,对右边有负贡献。类似地,矩形的左边对点有正贡献,右边对点有负贡献。在统计的时候用树状数组维护这个贡献的区间和即可。注意如果x相同,按照矩形左边界-点-矩形右边界的顺序来进行。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;

const int maxn = 4e5 + 10;

int n, o, x, y, _x, _y;
int no = 0, tot = 0, w[maxn];
int tree[maxn][2];
ll ans[maxn];
int lowbit(int x) {return x & (-x);}
struct node
{
	int op, id, x, l, r;
	bool operator < (const node& o) const
	{
		if(x != o.x) return x < o.x;
		return op < o.op;
	}
}e[maxn], now[maxn];

void add(int op, int p, int x)
{
	while(p < maxn)
	{
		tree[p][op] += x;
		p += lowbit(p);
	}
}

void clr(int op, int p)
{
	if(x <= 0) return;
	while(p < maxn)
	{
		tree[p][op] = 0;
		p += lowbit(p);
	}
}

int sum(int op, int p)
{
	int res = 0;
	while(p)
	{
		res += tree[p][op];
		p -= lowbit(p);
	}
	return res;
}

void solve(int l, int r)
{
	if(l == r) return;
	int mid = (l + r) >> 1;
	solve(l, mid), solve(mid + 1, r);
	vector<int> va, vb;
	int nu = 0, u = l, v = mid + 1;
	while(u <= mid && v <= r)
	{
		if(e[u] < e[v])
		{
			if(e[u].op == 2)
			{
				add(0, e[u].r, 1);
				va.push_back(e[u].r);
			}
			else if(e[u].op == 1)
			{
				add(1, e[u].l, 1);
				add(1, e[u].r + 1, -1);
				vb.push_back(e[u].l);
				vb.push_back(e[u].r + 1);
			}
			else if(e[u].op == 3)
			{
				add(1, e[u].l, -1);
				add(1, e[u].r + 1, 1);
				vb.push_back(e[u].l);
				vb.push_back(e[u].r + 1);
			}
			now[nu++] = e[u++];
		}
		else
		{
			if(e[v].op == 2) ans[e[v].id] += sum(1, e[v].r);
			else if(e[v].op == 1) ans[e[v].id] -= sum(0, e[v].r) - sum(0, e[v].l-1);
			else if(e[v].op == 3) ans[e[v].id] += sum(0, e[v].r) - sum(0, e[v].l-1);
			now[nu++] = e[v++];
		}
	}
	while(u <= mid) now[nu++] = e[u++];
	while(v <= r)
	{
		if(e[v].op == 2) ans[e[v].id] += sum(1, e[v].r);
		else if(e[v].op == 1) ans[e[v].id] -= sum(0, e[v].r) - sum(0, e[v].l-1);
		else if(e[v].op == 3) ans[e[v].id] += sum(0, e[v].r) - sum(0, e[v].l-1);
		now[nu++] = e[v++];
	}
	for(int i = 0;i < va.size();i++) clr(0, va[i]);
	for(int i = 0;i < vb.size();i++) clr(1, vb[i]);
	for(int i = l;i <= r;i++) e[i] = now[i-l];
}

int main()
{
	scanf("%d", &n);
	w[no++] = 0;
	for(int i = 1;i <= n;i++)
	{
		scanf("%d%d%d", &o, &x, &y);
		if(o == 1)
		{
			w[no++] = ++x, w[no++] = ++y;
			e[tot++] = (node){2, i, x, 0, y};
		}
		else
		{
			scanf("%d%d", &_x, &_y);
			w[no++] = ++x, w[no++] = ++y, w[no++] = ++_x, w[no++] = ++_y;
			e[tot++] = (node){1, i, x, y, _y};
			e[tot++] = (node){3, i, _x, y, _y};
		}
	}
	sort(w, w + no);
	no = unique(w, w + no) - w;
	for(int i = 0;i < tot;i++)
	{
		e[i].x = lower_bound(w, w + no, e[i].x) - w;
		e[i].l = lower_bound(w, w + no, e[i].l) - w;
		e[i].r = lower_bound(w, w + no, e[i].r) - w;
	}
	solve(0, tot - 1);
	for(int i = 1;i <= n;i++)
	{
		ans[i] += ans[i-1];
		printf("%I64d\n", ans[i]);
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值