Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)
只有C题的题解哟
C. Hungry Games
思路:求这么多区间数,一般都会考虑动态规划,我们将dp[i]记为左端为i时满足题目要求的区间数,我们考虑这样一个问题:当我们以毒性值为0时去找蘑菇, 很明显当我们找到<=x的最后一个蘑菇时,下一个就要清零,这时候我们就又变成了毒性值为0时去找蘑菇,所以关键就是找到这个节点,让毒性值清0。记这个点为j,那从j+1开始就又变成了相同的问题。
所以状态转移方程为:dp[ i ] = dp[ j] +j -i-1; 如何寻找那个大于x的最小的j呢,我们只需要upper_bound去寻找就好了,
#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
#define endl '\n'
void fast ()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);}
const int maxn = 2e5+100;
//#define pii pair<int,int>
void solve ()
{
int n,k ;cin >> n >> k;
vector<int> a(n+1);
for(int i=1;i<=n;i++){
cin >> a[i];
}
partial_sum(a.begin()+1,a.end(),a.begin()+1); //求前缀和
vector<int> dp(n+2,0);
for(int i=n-1;i>=0;i--){ //dp[i]表示i为左端点的区间数
int j = upper_bound(a.begin(),a.end(),a[i]+k)-a.begin();
dp[i] = dp[j]+j-i-1;
}
cout<<accumulate(dp.begin(),dp.end(),0ll)<<endl; //注意这里必须开ll
}
signed main ()
{
fast();
int t; cin >>t;
while(t--){
solve();
}
return 0;
}
B. Array Craft
思路:将x,y两边设置为以-1为首的正负1交替,x,y内部全为1即可
void solve ()
{
int n,x,y; cin >>n>>x>>y;
vector<int>ans(n+1,0);
for(int i=x+1;i<=n;i++){ //将两边设为正负1交替
if((x+1)%2 ==1){
if(i%2==1) ans[i] =-1;
else ans[i] =1;
}
else {
if(i%2 ==0) ans[i] =-1;
else ans[i] =1;
}
}
for(int i=y-1;i>=1;i--){ //将两边设为正负1交替
if((y-1)%2 ==1){
if(i%2==1) ans[i] =-1;
else ans[i] =1;
}
else {
if(i%2 ==0) ans[i] =-1;
else ans[i] =1;
}
}
for(int i=y;i<=x;i++){ //中间设定为1
ans[i] =1;
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
cout<<endl;
}
C. Mad MAD Sum
思路:我们先考虑一组数据:1 1 2 2 3 3我们会发现,通过变换,每次变换之后数组为:
0 1 1 2 2 3 ->0 0 1 1 2 2 ->0 0 0 1 1 2->.......
当数组中有重复出现数字,每次数组都是向右平移,这样的规律让我们计算每次数组的sum时不必要去再遍历数组。
那什么时候我们可以获得这样的所有数字都重复出现的数组呢?
我们会发现只要正常进行两次操作,所有数组都会变成所有数字至少出现两次的情况
AC代码:
#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
#define endl '\n'
void fast ()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);}
const int maxn = 2e5+10;
//#define pii pair<int,int>
int vis[maxn];
void solve ()
{
int n; cin >>n;
vector<int> a(n+1,0);
int sum =0;
int mad =0; //最大重复出现数
int ans =0;
for(int i=1;i<=n;i++){
cin >> a[i];
ans+=a[i];
}
for(int i=1;i<=n;i++){ //进行第一次变换
vis[a[i]]++;
if(vis[a[i]] >1){
mad = max(mad,a[i]);
}
a[i] =mad;
}
for(int i=1; i<=n;i++){ //变换一次之后ans
ans+=a[i];
}
memset(vis,0,sizeof(vis));
mad = 0;
for(int i=1;i<=n;i++){ //进行第二次变换
vis[a[i]]++;
if(vis[a[i]] >1){
mad = max(mad,a[i]);
}
a[i] =mad;
}
/*for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;*/
for(int i=1; i<=n;i++){ //变换二次之后ans
sum+=a[i]; //sum第二次变换之后的和
}
//cout<<sum<<" "<<ans<<endl;
for(int i=n;sum!=0;i--){
ans+=sum;
sum-=a[i];
}
cout<<ans<<endl;
memset(vis,0,sizeof(vis));
}
signed main ()
{
fast();
int t; cin >>t;
while(t--){
solve();
}
return 0;
}