[日常摸鱼]bzoj1271秦腾与教学评估-二分

https://darkbzoj.tk/problem/1271

题意:一个无限长的数列,下标从1开始,开始全为0,现在给(n)个操作((s_i,e_i,d_i))表示让(s_i,s_i+d_i,s_i+2d_i,dots,s_i+kd_i(s_i+kd_ileq e_i &s_i+(k+1)d_i>e_i))这些位置+1,同时保证最后至多有一个位置的值是奇数,回答是否存在这样的位置,如果存在则输出位置。

题解:至多只有一个位置非常关键,虽然原数列可能是:偶…偶奇偶…,但是考虑前缀和的话就会变成:偶…偶奇奇…,好的有二分性质了…

现在就只要考虑给一个位置(x)怎么快速求对应的前缀和就行,发现也很简单…如果不考虑(e_i)这个东西,那第(i)个操作的贡献就直接是(egin{aligned}1+lfloorfrac{x-s_i}{d_i} floorend{aligned}),有(e_i)的话(x)变成(min(x,e_i))就行啦…于是就能(O(nlog m))地做了,(m)是值域。


#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;
const int N=2e5+5;
const int M=(~0u>>1);
int n,T;
int S[N],D[N],E[N];
ll check(int x)
{
	ll res=0;
	rep(i,1,n)if(x>=S[i])
	{
		if(x>=E[i])res+=1ll*(1+(E[i]-S[i])/D[i]);
		else res+=1ll*(1+(x-S[i])/D[i]);
	}
	return res;
}
int main()
{
	scanf("%d",&T);
	rep(tc,1,T)
	{
		scanf("%d",&n);
		rep(i,1,n)scanf("%d%d%d",&S[i],&E[i],&D[i]);
		ll l=0,r=M;
		while(r>l)
		{
			ll mid=(l+r)>>1;
			ll t=check(mid);
			if(t&1ll)r=mid;
			else l=mid+1;
		}
		ll t=check(l);
		if(t&1ll)printf("%d %lld
",l,check(l)-check(l-1));
		else printf("Poor QIN Teng:(
");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/yoshinow2001/p/14467007.html