【比赛链接】http://59.61.75.5:8018/contest/224
A. 雕像
【题解】
一般最大最小问题可用二分解决,而和的最小值/最大值类问题可用 dp 凸优化(wqs 二分)解决.
外层使用 dp 凸优化,内层 dp 符合要求覆盖所有村庄需要的最少雕像个数,由于满足四边形不等式,单调队列+二分优化 dp 即可.
效率 $O(n \log n \log \sum\limits_{i=1}^n a_i)$.
【代码】
1 #include<bits/stdc++.h> 2 const long long inf=1LL<<60; 3 const int maxn=300000+10; 4 long long sum[maxn],f[maxn]; 5 int a[maxn],g[maxn],q[maxn],pos[maxn],n,k; 6 inline long long query ( int l,int r ) { return sum[l-1]+sum[r]-sum[(l+r)/2]-sum[(l+r-1)/2]; } 7 inline void solve ( long long k ) 8 { 9 for ( int i=1;i<=n;i++ ) f[i]=inf,g[i]=q[i]=pos[i]=0; 10 int l=1,r=1;pos[1]=n; 11 for ( int i=1;i<=n;i++ ) 12 { 13 while ( l<r and pos[l]<=i ) l++; 14 f[i]=f[q[l]]+query(q[l]+1,i)+k; 15 g[i]=g[q[l]]+1; 16 while ( l<r ) 17 { 18 long long lst=f[q[r]]+query(q[r]+1,pos[r-1]); 19 long long res=f[i]+query(i+1,pos[r-1]); 20 if ( lst<res or ( lst==res and g[q[r]]<=g[i] ) ) break; 21 r--; 22 } 23 int ll=q[r],rr=n+1; 24 while ( ll<rr ) 25 { 26 int mid=(ll+rr)>>1; 27 long long lst=f[q[r]]+query(q[r]+1,mid); 28 long long res=f[i]+query(i+1,mid); 29 if ( lst>res or ( lst==res and g[q[r]]>g[i] ) ) rr=mid; 30 else ll=mid+1; 31 } 32 pos[r]=ll;q[++r]=i; 33 } 34 } 35 signed main() 36 { 37 scanf("%d%d",&n,&k); 38 for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i]; 39 long long l=0,r=sum[n]; 40 while ( l<r ) 41 { 42 long long mid=(l+r)>>1; 43 solve(mid); 44 if ( g[n]>k ) l=mid+1; 45 else r=mid; 46 } 47 solve(l); 48 return !printf("%lld\n",f[n]-k*l); 49 }
B. 函数
【题解】
显然 $H(d)=\prod\limits_{p\text{ is prime}} g(p,d)$ 是积性函数。由二平方和定理(打表找规律)可知 $H(p^e)=3e+1\,(p\equiv 1\pmod 4)$,Min_25 筛即可.
【代码】
1 #include<bits/stdc++.h> 2 long long n,m,pr[100000],tot,Pow[100000],g[100000],S1[100000][5],S2[100000][5],sum1[100000],sum2[100000]; 3 bool flag[100000]; 4 inline long long f ( long long p,long long k ) { return (p%4==1) ? 3*k+1 : 1 ; } 5 inline long long Min_25 ( long long x,long long k ) 6 { 7 if ( x<=1 or pr[k]>x ) return 0; 8 long long ans=((x<=m)?sum1[x]:sum2[n/x])-Pow[k-1]; 9 for ( long long i=k;i<=tot and pr[i]*pr[i]<=x;i++ ) for ( long long v=pr[i],j=1;v*pr[i]<=x;v*=pr[i],j++ ) ans+=Min_25(x/v,i+1)*f(pr[i],j)+f(pr[i],j+1); 10 return ans; 11 } 12 signed main() 13 { 14 long long T; 15 for ( scanf("%lld",&T);T--; ) 16 { 17 scanf("%lld",&n);m=(long long)sqrt(n); 18 for ( long long i=1;i<=m;i++ ) flag[i]=true; 19 flag[1]=false;tot=0; 20 for ( long long i=2;i<=m;i++ ) 21 { 22 if ( flag[i] ) pr[++tot]=i,Pow[tot]=Pow[tot-1]+((i%4==1)?4:1); 23 for ( long long j=1;j<=tot and pr[j]*i<=m;j++ ) 24 { 25 flag[i*pr[j]]=false; 26 if ( !(i%pr[j]) ) break; 27 } 28 } 29 for ( long long i=1;i<=n;i=n/(n/(i+1)) ) 30 { 31 if ( i<=m ) for ( long long j=0;j<4;j++ ) S1[i][j]=(i-1)/4+((i-1)%4>=(j+3)%4); 32 else for ( long long j=0;j<4;j++ ) S2[n/i][j]=(i-1)/4+((i-1)%4>=(j+3)%4); 33 if ( i==n ) break; 34 } 35 for ( long long i=1;i<=tot;i++ ) 36 { 37 long long p=pr[i],p2=p*p; 38 for ( long long a=n;a>=p2;a=n/(n/a+1) ) for ( long long j=0;j<4;j++ ) 39 if ( a<=m ) 40 { 41 if ( a/p<=m ) S1[a][p*j%4]-=S1[a/p][j]-S1[p-1][j]; 42 else S1[a][p*j%4]-=S2[n/(a/p)][j]-S1[p-1][j]; 43 } 44 else 45 { 46 if ( a/p<=m ) S2[n/a][p*j%4]-=S1[a/p][j]-S1[p-1][j]; 47 else S2[n/a][p*j%4]-=S2[n/(a/p)][j]-S1[p-1][j]; 48 } 49 } 50 for ( long long i=1;i<=n;i=n/(n/(i+1)) ) 51 { 52 if ( i<=m ) S1[i][1]--; 53 else S2[n/i][1]--; 54 if ( i==n ) break; 55 } 56 for ( long long i=1;i<=n;i=n/(n/(i+1)) ) 57 { 58 if ( i<=m ) sum1[i]=4*S1[i][1]+S1[i][3]+1; 59 else sum2[n/i]=4*S2[n/i][1]+S2[n/i][3]+1; 60 if ( i==n ) break; 61 } 62 printf("%lld\n",Min_25(n,1)+1); 63 } 64 return 0; 65 }
C. Incomparable Pairs
【题解】
考虑容斥,计算子串 $a$ 为子串 $b$ 的子串数量.
对 $s$ 建 SAM,对于每一个一个本质不同的子串,计算它包含的本质不同的子串个数之和就是答案.
一个子串可能出现多次,任意选取一次就可以计算答案. 而对于自动机上的一个节点,它产生的贡献为 $s_{\min,r},s_{\min+1,r},\dots,s_{\max,r}$ 中的本质不同的子串.
设 $p_i$ 表示从 $i$ 开始,到现在 $r$ 的本质不同子串的个数(位置不同的相同子串只记录最后一次),可以对每一个本质不同的字串,在它最后一次出现的左端点 $+1$.
每插入一个字符,就算一次新增的自动机节点的贡献,当 $r$ 变大的时候,在 parent 树上从新增节点到根的最后出现位置都会更改。相当于把 parent 树上这一条链染色. 用 LCT 维护.
发现我们要求的是后缀和后的区间和,可以在线段树上区间加等差数列.
发现 SAM 上节点可能分裂,这里查询答案的时候用原来的大小,查询这个节点原来的 $[R-\max+1,R-\min+1]$. 累加就是答案.
效率 $O(n \log^2 n)$.