题解【CF549F Yura and Developers】
作者:互联网
楼下的两篇题解都看不懂,不会笛卡尔树,不会打标找规律。
但是 $\texttt{CDQ}$ 分治你值得拥有。
对于分治的两个区间 $[l,mid]$ 和 $(mid,r]$,对于分别位于两个区间的点 $j,i$,计算是否有贡献。
设 $s_i$ 表示前缀/后缀和,$b_i$ 表示前缀/后缀最大值。
则可以将题目条件写成另一种形式:$s_j+s_i -\max (b_i,b_j)\equiv 0\pmod k$。
对于取 max 操作,我们最常见的手法就是分情况讨论。
这里只讨论 $b_j>b_i$ 的情况,另一种情况留给读者分析。
那么原式化为 $s_j+s_i -b_j\equiv 0\pmod k$。
令 $c_i =s_j -b_j$,则原式变为 $s_i+c_j \equiv 0\pmod k$。
那么本题可以转化为,对于 $b_j>b_i$ 的所有下标 $j$,有多少个满足与 $c_j+s_i\equiv 0\pmod k$。
这就很简单了,我们对 $[l,mid]$ 和 $(mid,r]$ 分别按照 $b_i$ 排序,然后一个 Two-Pointers 两边扫过去就行了。
单点修改单点查询的问题我竟然一开始 nt 写了树状数组然后被卡常了。
这样复杂度是 $\Theta(n\log^2 n)$,瓶颈在于排序。
能不能更快?
答案是可以的。
我们排序的 $b$ 是一个前缀/后缀最大值,它是具有单调性的,所以只需要区间反转,这是 $\Theta(n\log n)$ 的!
但你会发现不管是 $\Theta(n\log n)$ 还是 $\Theta(n\log ^2 n)$ 都是最劣解。
两次速度对比。
放一下代码(需要针对 $b$ 的大小关系做两次 $\texttt{CDQ}$,所以有点长):
#include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<vector> #include<cstring> #include<ctime> #include<cstdlib> #include<cctype> #include<stack> #include<map> #include<climits> #include<set> #include<iostream> #define rint() read<int>() #define rll() read<ll>() #define rep(i,a,b) for(register int i=a;i<=b;++i) #define rev(i,a,b) for(register int i=a;i>=b;--i) #define gra(i,u) for(register int i=head[u];i;i=edge[i].nxt) #define print(a,l,r) rep(i,l,r) printf("%d ",a[i]); #define printll(a,l,r) rep(i,l,r) printf("%lld ",a[i]); #define Clear(a) memset(a,0,sizeof(a)) #define yes puts("YES") #define no puts("NO") #define yyds puts("YYDS") using namespace std; typedef long long ll; inline int read() { register int s=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9')s=s*10+(ch-'0'),ch=getchar(); return s*w; } const int INF=1e9; const ll LLINF=1e18; template<typename T> inline T Min(T x,T y){return x<y?x:y;} template<typename T> inline T Max(T x,T y){return x>y?x:y;} template<typename T> inline void Swap(T&x,T&y){int t=x;x=y;y=t;return;} const int MAXN(3e5+10); const int MAXM(1e6+10); int n,k,a[MAXN],b[MAXN]; ll s[MAXN],c[MAXN]; struct Lsh{ll val,idx;}; Lsh lsh[MAXN]; inline bool cmp(Lsh x,Lsh y){return x.val<y.val;} struct node{int s,c;int b;}; node h[MAXN]; inline bool cmp2(node x,node y){return x.b<y.b;} inline bool cmp4(node x,node y){return x.b>y.b;} inline void LSH(int l,int r) { rep(i,l,r) lsh[i].idx=i,lsh[i].val=b[i]; sort(lsh+l,lsh+r+1,cmp); int v(0); lsh[l-1].val=-1; rep(i,l,r) if(lsh[i].val!=lsh[i-1].val) b[lsh[i].idx]=++v;else b[lsh[i].idx]=v; return; } int bit[MAXM]; inline ll CDQ(int l,int r) { if(l==r) return 0; int mid=(l+r)>>1; ll ans=CDQ(l,mid)+CDQ(mid+1,r); b[mid]=a[mid],s[mid]=a[mid],b[mid+1]=a[mid+1],s[mid+1]=a[mid+1]; rev(i,mid-1,l) b[i]=Max(a[i],b[i+1]),s[i]=s[i+1]+(ll)a[i]; rep(i,mid+2,r) b[i]=Max(a[i],b[i-1]),s[i]=s[i-1]+(ll)a[i]; rep(i,l,r) c[i]=s[i]-(ll)b[i]; rep(i,l,r) { c[i]%=k,s[i]=(-s[i]%k+(ll)k)%k; h[i].c=(int)c[i]+1,h[i].s=(int)s[i]+1,h[i].b=b[i]; } reverse(h+l,h+mid+1); int j(l); rep(i,mid+1,r) { while(j<=mid&&h[j].b<h[i].b) ++bit[h[j].s],++j; ans+=(ll)bit[h[i].c]; } rep(i,l,j-1) --bit[h[i].s]; return ans; } inline ll CDQ2(int l,int r) { if(l==r) return 0; int mid=(l+r)>>1; ll ans=CDQ2(l,mid)+CDQ2(mid+1,r); b[mid]=a[mid],s[mid]=a[mid]; b[mid+1]=a[mid+1],s[mid+1]=a[mid+1]; rev(i,mid-1,l) b[i]=Max(a[i],b[i+1]),s[i]=s[i+1]+(ll)a[i]; rep(i,mid+2,r) b[i]=Max(a[i],b[i-1]),s[i]=s[i-1]+(ll)a[i]; rep(i,l,r) { c[i]=((b[i]-s[i])%k+(ll)k)%k,s[i]%=k; h[i].c=(int)c[i]+1,h[i].s=(int)s[i]+1,h[i].b=b[i]; } reverse(h+mid+1,h+r+1); int j(l); rep(i,mid+1,r) { while(j<=mid&&h[j].b>=h[i].b) ++bit[h[j].c],++j; ans+=bit[h[i].s]; } rep(i,l,j-1) --bit[h[i].c]; return ans; } int main() { // freopen("read.txt","r",stdin); n=read(),k=read(); rep(i,1,n) a[i]=read(); printf("%lld\n",CDQ(1,n)+CDQ2(1,n)); return 0; } /* 4 3 1 2 3 4 //3 4 2 4 4 7 4 //6 5 6 3 3 12 8 10 //3 */
标签:include,ll,int,题解,rep,mid,lsh,Developers,Yura 来源: https://www.cnblogs.com/UperFicial/p/16377512.html