1. 程式人生 > >Gym - 101611H - 思維+數學

Gym - 101611H - 思維+數學

題目連結:https://vjudge.net/problem/Gym-101611H

 

解題思路:

由於我們得到了m個位置及他的值,那麼可以將原區間分成m+1個區間,對這些個區間求區間可以取到的最小值和最大值,最後累加最小值和最大值,看T是否在其中就是他到底有沒有解.

最小值求法:

對於一個區間的左端點b[i-1]和右端點b[i]讓從b[i-1]一直下降一格直到不能下降後最後一直上升到b[i]他們的累加和就是最小值,

累加和可以用兩個部分的等差數列求和來求.

最大值求法:將求最小值的先降後升改為先升後降就是求最大值的方法.

假設區間長度為x,區間高度差為y,m是左端點到極點(極大或極小)的區間長度,n是右端點到極端的區間長度

那麼有:

1. x = m + n,b[i-1] - m = b[i] -  n,b[i]-b[i-1] = n - m = y

聯立x = m + n,y = n - m可以求得m和n.求最大值的m和n恰好是相反的(可以再次聯立方程求,也可以根據平行四邊形法則得到)

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mx = 1e5+5;
#define fi first
#define se second
ll a[mx];
ll b[mx];
ll calc(ll a,ll b){
	return (a+b)*(b-a+1)/2;
}
ll calc1(ll a1,ll b1,ll a2,ll b2){
	ll x = a2 - a1;
	ll y = b2 - b1;
	ll len2 = (x+y)/2,len1 = (x-y)/2;
	ll ans1 = b1 - len1,ans2 = b2 - len2;
	ans1 = max(1ll*0,ans1); ans2 = max(1ll*0,ans2);
	ll ans = calc(ans1,b1)+calc(ans2,b2);
	if(a1+len1==a2-len2) ans -= ans1;
	return ans;
}
ll calc2(ll a1,ll b1,ll a2,ll b2){
	ll x = a2 - a1;
	ll y = b2 - b1;
	ll len1 = (x+y)/2,len2 = (x-y)/2;
	ll ans1 = b1 + len1,ans2 = b2 + len2;
	ll ans = calc(b1,ans1)+calc(b2,ans2);
	if(a1+len1==a2-len2) ans -= ans1;
	return ans; 
}
int main(){
	ll T,n,m;
	scanf("%lld%lld%lld",&T,&n,&m);
	ll mi = 0;
	ll ma = 0;
	bool ok = true;
	for(ll i = 1; i <= m; i++){
		scanf("%lld%lld",&a[i],&b[i]);
		if(i==1){
			mi += calc(max(0ll,b[i]-a[i]+1),b[i]);
			ma += calc(b[i],b[i]+a[i]-1);
		}
		else{
			mi += calc1(a[i-1],b[i-1],a[i],b[i]);
			ma += calc2(a[i-1],b[i-1],a[i],b[i]);
			if(abs(b[i]-b[i-1])>a[i]-a[i-1])
				ok = false;
		}
	}
	mi += calc(max(0ll,b[m]-n+a[m]),b[m]);
	ma += calc(b[m],b[m]+n-a[m]);
	for(int i=1;i<=m;i++) mi -= b[i],ma -= b[i];
	if(ok&&T>=mi&&T<=ma)
		puts("Yes");
	else
		puts("No");	
	return 0;
}