AtCoder Beginner Contest 175 D.Moving Piece

AtCoder Beginner Contest 175 D.Moving Piece

题目链接

AtCoder Beginner Contest 175 D.Moving Piece_第1张图片
最近忙于复习,今天实在复习不下去了就来补一下这题,比赛时没有考虑周全,WA了8个点,其实就差一点了,这题我的思路是这样的:
首先计算每个循环节的长度,对某段长度 l e n len len,求出 [ 1 , l e n ] [1,len] [1,len] 区间内的前缀和,这是预处理过程,很容易实现。题目的难点在于至多 k k k 次而非正好 k k k 次,所以要分情况讨论,对某个循环节长度 l e n len len

  • k < = l e n k<=len k<=len,很简单,取最大前缀和即可~
  • k > l e n k>len k>len,我们要仔细考虑,此时如果前缀和 s u m [ l e n ] ≤ 0 sum[len]\leq0 sum[len]0,那么我们取的循环节越多答案无疑越小,所以只需要取最大前缀和即可,也即和第一种情况一样;如果前缀和 s u m [ l e n ] > 0 sum[len]>0 sum[len]>0,此时我们发现循环节取得越多答案就越大,最多取的个数无疑是 n u m = ⌊ k l e n ⌋ num=\lfloor\frac{k}{len}\rfloor num=lenk,那么就相当于选一个 k % l e n k\%len k%len 里的最大前缀和加上前面 n u m num num 个前缀和的和,即 a n s = m a x { s u m [ k % l e n ] } + n u m ∗ s u m [ l e n ] ans=max\lbrace sum[k\%len]\rbrace+num*sum[len] ans=max{sum[k%len]}+numsum[len]此处有个小细节,就是当 k % l e n = 0 k\%len=0 k%len=0 时,我们个数要减 1 1 1,空一个循环节出来,以便和上面的取余统一,其实答案并没有变化,简便写法而已~

反正是挺考验思维的,好题b( ̄▽ ̄)d ,AC代码如下:

#include
using namespace std;
typedef long long ll;
const int N=5e3+5;
int n;
ll k,a[N],p[N],cnt[N]={0},sum[N][N]={0};
ll dfs(ll i){
    ll u=i,num=1,pos=1;
    while(p[i]!=u){
        num++;
        i=p[i];
        sum[u][pos]=sum[u][pos-1]+a[i];
      pos++;
    }
    sum[u][pos]=sum[u][pos-1]+a[u];
    return num;
}

int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>p[i];
    for(int i=1;i<=n;i++) cin>>a[i];
    ll ans=-1e18;
    for(int i=1;i<=n;i++){
        cnt[i]=dfs(i);
        ll mx1=-1e18,mx2=-1e18;
        for(int j=1;j<=cnt[i];j++){
            mx1=max(mx1,sum[i][j]);
        }
        for(int j=1;j<=(k%cnt[i]?k%cnt[i]:cnt[i]);j++){
            mx2=max(mx2,sum[i][j]);
        }
        if(k>cnt[i]){
            ll num=(k%cnt[i])?k/cnt[i]:k/cnt[i]-1;
            ans=max(ans,max(mx1,num*sum[i][cnt[i]]+mx2));
        }
        else ans=max(ans,mx2);
    }
    cout<<ans;
}

你可能感兴趣的:(前后缀和,思维,AtCoder)