Edu10

Edu10

传送门

C:一个n排列,其中有m对数,问有多少个区间满足区间里不含成对的数

对于一对数对应的下标(l,r),若一个区间左端点为l,那么右端点必须<r,否则就含有(l,r)这一对了.遍历一遍后,发现会有[l,r-1]中包含其他pair的情况,就需要从后往前遍历,i位置到达的最远下标不能大于i+1位置到达的最远下标,不然就包含了i+1那一段.

int n,m;
int vis[N]; //i对应下标
int dp[N];  //下标i能到的最远位置
void work() {
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) {
		int v;
		scanf("%d",&v);
		vis[v]=i;
		dp[i] = n+1;
	}
	while(m--) {
		int l,r;
		scanf("%d%d",&l,&r);
		if(vis[l] > vis[r])swap(l,r);
		dp[vis[l]] = min(dp[vis[l]],vis[r]);
	}
	for(int i=n-1; i>=1; i--) {
		dp[i] = min(dp[i],dp[i+1]);
	}
	ll ans =0;
	for(int i=1; i<=n; i++) {
		ans += dp[i] - i;
	}
	printf("%lld
",ans);
}

D.树状数组+离散化

题意:一堆线段,问你每条线段内"包含"了几条其他线段.

将线段按照右端点升序排序,对于线段i和i-1,因为i.r >= (i-1).r,所以若(i-1).l >= i.l,那么i-1线段就在i里面.

同理,对于线段x,只需看1~x-1中有多少左端点 >= x.l.

把左端点用树状数组维护,sum(x.r)-sum(x.l-1)表示包含的左端点个数.(注意开两倍空间....WA了无数次)

const int N = 4e5 + 50;

int n;
int l[N],r[N];
int lsh[N],tot =0;
int c[N];

int lowbit(int n) {
	return n & (-n);
}
void update(int x,int y) {
	for(; x<=N; x+=lowbit(x))c[x] += y;
}
int sum(int x) {
	int ans =0 ;
	for(; x; x-=lowbit(x))ans += c[x];
	return ans;
}

struct node {
	int l,r;
	int id;
} p[N];

bool cmp(node a,node b) {
	if(a.r != b.r)return a.r < b.r;
	return a.l > b.l;
}
int ans[N];

void work() {
	scanf("%d",&n);
	for(int i=0; i<n; i++) {
		scanf("%d%d",&p[i].l,&p[i].r);
		if(p[i].l > p[i].r)swap(p[i].r,p[i].l);
		p[i].id = i;
		lsh[tot++] = p[i].l;
		lsh[tot++] = p[i].r;
	}
	sort(lsh,lsh+tot);
	tot = unique(lsh,lsh+tot)-lsh;
	sort(p,p+n,cmp);

	for(int i=0; i<n; i++) {
		int lll = lower_bound(lsh,lsh+tot,p[i].l)-lsh +1;
		int rrr = lower_bound(lsh,lsh+tot,p[i].r)-lsh+1;
		if(lll > rrr)swap(lll,rrr);
		int l1 = sum(lll-1);
		int r1 = sum(rrr);
		ans[p[i].id] = r1-l1;
		update(lll,1);
	}
	for(int i=0; i<n; i++) {
		printf("%d
",ans[i]);
	}
}
原文地址:https://www.cnblogs.com/LaiYiC/p/14969447.html