https://codeforces.com/contest/1428/problem/F
vp的时候没想出来。。。主要D想太久了,其实F也不太会
这题的关键是想到对于一个确定的r,l越左高度f(l,r)越高
于是就能想到每次r向右拓展一格,如果是1个新的1,那么左边就有一部分要全增加1
于是一个很简单想法就是在线段树上二分找到覆盖到哪里,然后区间覆盖,然后求个前缀和就是这个当前r对应的所有f(l,r)之和
然而这样十分难写
于是我们发现每次增加只增加1,且我们可以考虑对当前末尾最长的连续的1,[l,r],这一段是全部要+1的,然后l左边的就尝试用r-l+1去覆盖他们,我们就可以用rid[i]表示高度为i的最右边的端点在哪,每次r新增一个1,就把rid[r-l+1]=l,然更新一下总和就行了,[l+1,r]这一段是一个等差数列可以直接算,当s[r+1]='0'时,再更新一下rid[1]-rid[r-l+1]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;const int maxl=5e5+10;int n,m,cnt,tot,cas;ll ans;
int a[maxl];ll rid[maxl];
bool vis[maxl];
char s[maxl];inline void prework()
{scanf("%d",&n);scanf("%s",s+1);
}inline void mainwork()
{ll tmp=0,l=1,r;ans=0;for(int i=1;i<=n;i++)if(s[i]=='0')ans+=tmp,l=i+1,rid[0]=i;else{r=i;tmp-=(rid[r-l]-rid[r-l+1])*(r-l);tmp+=(l-rid[r-l+1])*(r-l+1);rid[r-l+1]=l;tmp+=r-l;ans+=tmp; if(s[i+1]!='1' && i+1<=n){for(int j=1;j<=r-l;j++)rid[j]=r-j+1;}}
}inline void print()
{printf("%lld",ans);
}int main()
{int t=1;//scanf("%d",&t);for(cas=1;cas<=t;cas++){prework();mainwork();print();}return 0;
}