洛谷 P10893 城市化发展委员会 题解
OIer_Eternity · · 题解
Description
定义一个序列为「安全的」当且仅当其所有前缀和均大于
先给定一个长度为
- 第
i 次操作将会由A_{i-1} 得到A_i 。 - 重复如下操作
n 次:- 若
A_{i-1} 是「安全的」就将其接到A_i 末尾。 - 将
A_{i-1} 循环左移1 位。
- 若
求
Solution
首先考虑
我们只需统计满足以第
则我们需要用到前缀和的极小值,线段树或树状数组维护即可。
接着拓展到
首先,在前一个序列
那么有
接着考虑是否会出现新的「被标记的」位置。
如图,若设红色的圈表示在
那么在
因此
AC Code
#include <bits/stdc++.h>
using namespace std;
const int p=998244353;
int n,k;
long long a[2000005];
struct Node{
int l,r;
long long Min;
}tree[8000005];
void build(int p,int l,int r){
tree[p].l=l,tree[p].r=r;
if (l==r){
tree[p].Min=a[l];
return;
}
int Mid=(l+r)>>1;
build(p<<1,l,Mid);
build(p<<1|1,Mid+1,r);
tree[p].Min=min(tree[p<<1].Min,tree[p<<1|1].Min);
}
long long query(int p,int l,int r){
if (l<=tree[p].l&&tree[p].r<=r) return tree[p].Min;
int Mid=(tree[p].l+tree[p].r)>>1;
long long res=1e18;
if (l<=Mid) res=query(p<<1,l,r);
if (r>Mid) res=min(res,query(p<<1|1,l,r));
return res;
}
int qpow(int a,int b){
int res=1;
for (;b;b>>=1,a=1ll*a*a%p) if (b&1) res=1ll*res*a%p;
return res;
}
int main(){
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i+n]=a[i];
for (int i=1;i<=2*n;i++) a[i]+=a[i-1];
build(1,1,2*n);
int ans=0;
for (int i=1;i<=n;i++) if (query(1,i,i+n-1)>a[i-1]) ans++;
for (int i=1;i<=k;i++) ans=1ll*ans*ans%p;
printf("%d\n",ans);
return 0;
}