CF1400E Clear the Multiset
一道经典简单的分治
由贪心可知,对于一段区间[L,R],一共有两种处理方式
1.一个一个减,次数为l-r+1
2.先区间减,直到最小的减没了,在考虑最小值隔开的两个区间。如果有多个最小值,其实也不影响,再往下分的时候一定会分开。区间答案就是 $min(l-r+1,f(l,p-1)+f(p+1,r)+minn)$ 具体含义见代码
#include<bits/stdc++.h> using namespace std; const int N=5e5+5; int a[N],n; int work(int l,int r){ if(l>r) return 0; if(l==r) return min(a[l],1); int minn=2e9+555,p; for(int i=l;i<=r;i++){ if(minn>a[i]){ minn=a[i];p=i; } } for(int i=l;i<=r;i++) a[i]-=minn; return min(r-l+1,work(l,p-1)+work(p+1,r)+minn); } int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; cout<<work(1,n); return 0; }
Distribution
Cakzy have a sequence a[1...n], and a[i] represents the number of candies in i-th candy pile. And every candy is the same. There are k children around Cakzy. Cakzy want to select an interval [l, r] (1 ≤ l ≤ r ≤ n) in the sequence. He will take away the maxmium pile in the interval [l, r]. Then he will mix the remaining candies of the interval [l, r] into one pile and distribute them equally to the k children. Attention:: One candy can not be divided into several pieces. Cazky wants to know how many intervals can meet his demand. Input The first line contains two integers n (1 ≤ n ≤ 3 × 105 ), m (1 ≤ m ≤ 3 × 106 ). Following one line contains n integers a[i] (1 ≤ a[i] ≤ 10000), denoting the number of candies in i-th pile. Output Output one line containing one decimal, denoting the answer. Example standard input standard output 4 3 1 2 3 4 3 Note In the example test, n = 4, m = 3. The 3 intervals are [1, 3], [1, 4], [3, 4].
一言以蔽之,就是对于一个区间,他合法当且仅当去掉最大值,他的和可以被区间长度整除。问总区间里有多少个子区间符合题意(必须连续)。
考虑分治。对于一个区间,我们只处理横跨中点的情况,左右子区间里的递归下去,这样就可以不重不漏处理所有情况。对于左区间,我们钦定一个最大值,向左扫,并记录后缀和,后缀最大值,然后享有,枚举,直到碰见更大的值,开一个桶,统计各个余数的个数,然后统计答案。同样的方法处理最大值在右区间的情况。注意处理右边之前,左边的情况数要减回来。
// ubsan: undefined // accoders #include<bits/stdc++.h> using namespace std; #define int long long const int N=3e6+66; int a[N],ans,cnt[N]; int n,m; void work(int l,int r){ if(l==r) return; int M=(l+r)>>1; work(l,M);work(M+1,r); int u=M,mxl=0,suml=0,mxr=0,sumr=0;//只处理横跨左右区间的情况, //其他情况直接递归下去 //左: for(int i=M;i>=l;i--){//相当于已经钦定了一个最大值 mxl=max(mxl,a[i]); suml+=a[i];//记录后缀和,后缀最大值 while(u<r&&a[u+1]<mxl){//向右枚举时不能大于钦定的最大值 u++; sumr=(sumr+a[u])%m; cnt[sumr]++;//余数为 sumr 的情况数 } ans+=cnt[(m-((suml-mxl)%m))%m]; } sumr=0; for(int i=M+1;i<=u;i++){ sumr=(sumr+a[i])%m; cnt[sumr]--; //处理右边之前,左边的情况数要减回来 } //右: 与左基本相同 u=M+1,mxl=0,suml=0,mxr=0,sumr=0; for(int i=u;i<=r;i++){ mxr=max(mxr,a[i]);sumr+=a[i]; while(u>l&&a[u-1]<=mxr){ u--; suml=(suml+a[u])%m; cnt[suml]++; } ans+=cnt[(m-((sumr-mxr)%m))%m]; } suml=0; for(int i=M;i>=u;i--){ suml=(suml+a[i])%m; cnt[suml]--; } } signed main(){ cin>>n>>m; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } work(1,n); cout<<ans; return 0; }
标签:CF1400E,int,Clear,work,suml,pile,区间,Multiset,最大值 From: https://www.cnblogs.com/DongPD/p/17501521.html