Gym - 101611H - 思維+數學
阿新 • • 發佈:2018-10-31
題目連結: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; }