[日常摸鱼]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_i\leq e_i \&s_i+(k+1)d_i>e_i)\)这些位置+1,同时保证最后至多有一个位置的值是奇数,回答是否存在这样的位置,如果存在则输出位置。

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

现在就只要考虑给一个位置\(x\)怎么快速求对应的前缀和就行,发现也很简单…如果不考虑\(e_i\)这个东西,那第\(i\)个操作的贡献就直接是\(\begin{aligned}1+\lfloor\frac{x-s_i}{d_i}\rfloor\end{aligned}\),有\(e_i\)的话\(x\)变成\(min(x,e_i)\)就行啦…于是就能\(O(n\log 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\n",l,check(l)-check(l-1));
		else printf("Poor QIN Teng:(\n");
	}
	return 0;
}
posted @ 2021-03-02 07:40  yoshinow2001  阅读(39)  评论(0编辑  收藏  举报