多区间选点,之和一定

问题描述

有多个区间 [ l , r ] [l,r] [l,r] ,在每个区间选一个点使得各点之和为 X X X

思路

每个区间选一个点组成的 x x x 的范围为 [ [ [ ∑ i = 1 n l i \sum_{i=1}^{n}{l_i} i=1nli , ∑ i = 1 n r i \sum_{i=1}^{n}{r_i} i=1nri ] ] ],若所求 X X X 在此区间则可达
而后从后往前反推求出一组解

步骤

l i l_i li r i r_i ri X = 10 X=10 X=10
1 3
1 5
2 3

1. L i = L_i= Li= ∑ j = 1 i l j \sum_{j=1}^{i}{l_j} j=1ilj , R i = R_i= Ri= ∑ j = 1 i r j \sum_{j=1}^{i}{r_j} j=1irj ,即对前 i i i 区间可达点集

L i L_i Li R i R_i Ri
0 0
1 3
2 8
4 11

2. a n s [ n ] = X ans[n]=X ans[n]=X,意味着前 n n n 个点和为 X ,而区间 n n n 可选范围为 [ 2 , 3 ] [2,3] [2,3] ,因此要求前 n − 1 n-1 n1 需要可达范围为 [ X − 3 = 7 , X − 2 = 8 ] [X-3=7,X-2=8] [X3=7,X2=8] 。而前 n − 1 n-1 n1 的可达范围是 [ 2 , 8 ] [2,8] [2,8] ,存在交集(可证必有交集),在交集中任选一点(选四个边界即可)作为前 n − 1 n-1 n1 取下来的和,即新的 X X X
3. 而后不断重复 2 2 2 操作直至 i = 1 i=1 i=1

代码

Codeforces Round #753 (Div. 3-G. Banquet Preparations 1)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+9;
int T,n;
LL m,tot;
LL sx[N],sy[N],l[N],r[N],L[N],R[N],ans[N];
LL Intersect(LL ltot,LL rtot,LL ltl,LL ltr)
{
	if(ltot>=ltl&&ltot<=ltr) return ltot;
	if(rtot>=ltl&&rtot<=ltr) return rtot;
	if(ltl>=ltot&&ltl<=rtot) return ltl;
	return ltr;
}
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>sx[i]>>sy[i];
		LL x=sx[i],y=sy[i];
		int miny=max(0ll,y-m);
		int minx=x-(m-(y-miny));
		l[i]=miny-minx;
		int maxx=max(0ll,x-m);
		int maxy=y-(m-(x-maxx));
		r[i]=maxy-maxx;
	}
	for(int i=1;i<=n;i++)
	{
		L[i]=L[i-1]+l[i];
		R[i]=R[i-1]+r[i];
	}
	if(0<L[n]) tot=L[n];
	else if(0>R[n]) tot=R[n];
	else
	{
		if(abs(L[n])%2==0) tot=0;
		else tot=1;
	}
	cout<<abs(tot)<<endl;
	for(int i=n;i>=1;i--)
	{
		LL ltot=tot-r[i],rtot=tot-l[i];
		LL ntot=Intersect(ltot,rtot,L[i-1],R[i-1]);
		ans[i]=tot-ntot;
		tot=ntot;
	}
	for(int i=1;i<=n;i++)
	{
		LL x=(sx[i]+sy[i]-m-ans[i])/2;
		cout<<sx[i]-x<<" "<<sy[i]-(x+ans[i])<<endl;
	}
}
int main()
{
	cin>>T;
	while(T--) solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值