二分答案。
对于每次二分后的答案来说,
先倍增序列,通过 two point 来找到 以每个点为起点的最优的符合答案的在哪里。
然后可以DFS树去判断他的前k祖先之间的距离是不是大于k。 常数有点大。
在 ID为 WuHongxun 的代码下学会了, 先判断一下最小一段的跨度, 如果跨度 >= n / k + 1 那么直接就是合法的了。
否则枚举最小跨度内的点作为起点, 直接走k步, 判断一下是否合法。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const int _inf = 0xc0c0c0c0; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL _INF = 0xc0c0c0c0c0c0c0c0; const LL mod = (int)1e9+7; const int N = 2e6 + 100; LL L = 1 << 30; int n, k, seed, add; LL d[N], s[N]; int nt[N]; inline LL cal(int l, int r){ return (s[r] - s[l+r>>1]) - (s[(l+r-1)>>1] - s[l-1]); } bool check(LL x){ int mn = 2 * n + 1, id; for(int i = 1, j = 1; i <= n * 2; ++i){ while(j <= 2 * n && cal(i,j) <= x) ++j; nt[i] = j; if(i <= n && j-i < mn){ mn = j - i; id = i; } } if(mn >= n/k + 1) return true; for(int i = id, ed = nt[i]%n+1; i != ed; i = i%n +1){ int z = i; for(int j = 1; j <= k; ++j){ z = nt[z]; if(i + n <= z) return true; } } return false; } void Ac(){ scanf("%d%d", &n, &k); scanf("%d%d", &seed, &add); for(int i = 1; i <= n; ++i){ seed = (seed * 239017 + add) & (L-1); d[i] = seed; } sort(d+1, d+1+n); for(int i = 1; i <= n; ++i) d[i+n] = d[i] + L; for(int i = 1; i <= 2*n; ++i) s[i] = s[i-1] + d[i]; LL l = 0, r = L * n / k; while(l <= r){ LL mid = l+r >> 1; if(!check(mid)) l = mid + 1; else r = mid - 1; } cout << l << endl; return ; } int main(){ Ac(); return 0; }