Codeforces Round 917 (Div. 2)
C. Watering an Array
题目链接
题意:
给定n,k,d
给n个数的数组a,第i个是 a i a_i ai
给k个数的数组v,第i个是 v i v_i vi
定义两个操作,
- a数组中每个 a i = i a_i=i ai=i 的给答案贡献1,a数组清零
- 如果现在是第x天,a数组中下标 1 ∼ v [ ( x − 1 ) % k + 1 ] 1\sim v[(x-1)\%k+1] 1∼v[(x−1)%k+1] 的位置都加1
每天必须且只能进行其中一个操作,问d天后最多能得多少分
思路:
好题,不过有点难。
发现操作1进行后a数组就会被清空,加上d相当大,所以感觉应该可以可以分为两部分,第一部分就是第1天从带初值的a到某天第一次清空a数组这一段,第二部分就是后面开始对一个固定的操作序列进行循环。不过操作序列v的值是不确定的,所以猜测循环的操作序列可能和v的值没有关系。
再研究研究发现,因为第二个操作是给1~vi下标位置的a增加1,所以,a数列是单调不增的,ai==i的要求是单调递增的,也就是说,不管搞几次操作2,最多只会产生一次贡献,那么不如进行完一次第二次操作就进行第一次操作,然后循环就行了。
这样我们就只需要去算前一部分就行了,发现通过前面说的循环操作只要两天就至少可以拿到1分,那么第一部分就一定不可能超过2*n天,否则光保底就有n分多了,而你第一部分最后一次收割,n个位置最多拿到n分,这样就不划算了。
一看数据范围,n=2000,第一部分直接暴力就行了
code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
int T,n,k,d;
int a[maxn],v[maxn];
int main(){
cin>>T;
while(T--){
cin>>n>>k>>d;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<k;i++)
cin>>v[i];
ll ans=0,cnt;
for(int i=0;i<=min(d-1,2*n);i++){
cnt=0;
for(int j=0;j<n;j++)cnt+=(a[j]==j+1);
ans=max(ans,cnt+(d-i-1)/2);
for(int j=0;j<v[i%k];j++)
a[j]++;
}
cout<<ans<<endl;
}
return 0;
}