HDU 6444 Neko's loop ( 2018 CCPC 网络赛 && 裴蜀定理 && 线段树 )
题意 : 给出一个 n 个元素的环、可以任意选择起点、选完起点后、可以行走 m 步、每次前进 k 个单位、所走到的点将产生正或负贡献、问你一开始得准备多少才能使得初始资金加上在环上获取最大利益不少于给定的 s
分析 :
对于一个环、固定步数下是有循环节的
不同循环节内的节点各不相同
根据裴蜀定理可得每个循环节的长度为 n / gcd(n, k)
所以共有 gcd(n, k) 个循环节
然后我们暴力扒出每一个循环节
循环节里面的元素放到一个新数组中、使其相邻
然后通过收尾相连接的方法模拟环
最后对这个收尾相连的数组求一下前缀和
就能知道从循环节起点开始到某一个位置可以产生的贡献
但是对于答案而言、有需要求最大子段和的情况
可以用线段树保存这个前缀和数组、然后通过线段树就可以找寻最小的前缀和位置
再用前缀和相减的方法来得到指定右端点的情况下的最大子段和
对于答案、需要讨论一下
尤其注意在取整串循环节得到贡献为正数情况下、循环节长度 < m 的情况
参见代码的 ans3 变量
#include<bits/stdc++.h> #define LL long long #define ULL unsigned long long #define scl(i) scanf("%lld", &i) #define scll(i, j) scanf("%lld %lld", &i, &j) #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k) #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l) #define scs(i) scanf("%s", i) #define sci(i) scanf("%d", &i) #define scd(i) scanf("%lf", &i) #define scIl(i) scanf("%I64d", &i) #define scii(i, j) scanf("%d %d", &i, &j) #define scdd(i, j) scanf("%lf %lf", &i, &j) #define scIll(i, j) scanf("%I64d %I64d", &i, &j) #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k) #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k) #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k) #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l) #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l) #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define lowbit(i) (i & (-i)) #define mem(i, j) memset(i, j, sizeof(i)) #define fir first #define sec second #define VI vector<int> #define ins(i) insert(i) #define pb(i) push_back(i) #define pii pair<int, int> #define VL vector<long long> #define mk(i, j) make_pair(i, j) #define all(i) i.begin(), i.end() #define pll pair<long long, long long> #define _TIME 0 #define _INPUT 0 #define _OUTPUT 0 clock_t START, END; void __stTIME(); void __enTIME(); void __IOPUT(); using namespace std; const int maxn = 1e5; const LL INF = 1e18; LL minv[maxn<<2]; int n, m, k; LL s, arr[maxn]; LL PreSum[maxn]; void PushUp(int rt) { minv[rt] = min(minv[rt<<1], minv[rt<<1|1]); } void Build(int l,int r,int rt) { if (l == r) { minv[rt] = PreSum[l]; return ; } int m = (l + r) >> 1; Build(lson); Build(rson); PushUp(rt); } LL Query(int L,int R, int l,int r,int rt) { if (L <= l && r <= R) { return minv[rt]; } LL ret = INF; int m = (l + r) >> 1; if (L <= m) ret = min( ret, Query(L , R , lson)); if (m < R) ret = min( ret, Query(L , R , rson)); PushUp(rt); return ret; } int main(void){__stTIME();__IOPUT(); int nCase; sci(nCase); for(int Case=1; Case<=nCase; Case++){ sci(n); scl(s); scii(m, k); for(int i=0; i<n; i++) scl(arr[i]); int num = __gcd(n, k); int len = n / num; LL ans = 0; for(int i=1; i<=num; i++){ int idx = i-1; for(int j=1; j<=len; j++){ PreSum[j] = PreSum[j+len] = arr[idx]; idx = (idx + k) % n; } for(int j=1; j<=(len<<1); j++) PreSum[j] += PreSum[j-1]; Build(1, len<<1, 1); LL Rem = 0; if(m%len != 0){ for(int j=len+1; j<=(len<<1); j++) Rem = max(Rem, PreSum[j] - Query(j-(m%len), j, 1, len<<1, 1)); } LL ans2 = 0; if(m >= len && PreSum[len] > 0) ans2 = Rem + (m / len) * PreSum[len]; LL ans3 = 0; for(int j=len+1; j<=(len<<1); j++) ans3 = max(ans3, PreSum[j] - Query(j-len, j, 1, len<<1, 1)); if(PreSum[len] > 0) ans3 += ((m-len)/len) * PreSum[len]; ans = max(ans, max(ans3, ans2)); } printf("Case #%d: %lld\n", Case, max(0LL, s - ans)); } __enTIME();return 0;} void __stTIME() { #if _TIME START = clock(); #endif } void __enTIME() { #if _TIME END = clock(); cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl; #endif } void __IOPUT() { #if _INPUT freopen("in.txt", "r", stdin); #endif #if _OUTPUT freopen("out.txt", "w", stdout); #endif }