最大前缀和
如果钦定某个位置为最大前缀和,设位置为 (pos) ,那么必然也是 ([1,pos]) 的最大前缀,并且 ([pos+1,n]) 的前缀和均 (<=0) .
(nleq 20) 可以考虑状压。设 (sum[S]) 表示前半部分选择状态为 (S) 时,前半部分序列总和,(f[S]) 表示状态为 (S) 时前半部分的方案数,(g[S]) 表示状态为 (S) 时后半部分的方案数。转移见代码。 完整代码
注意一下这边 (f) 数组的贡献方式,当且仅当 (f[S]) 合法(就是 (sum[S]ge 0) ) 时才能向 (f[S|(1<<i)]) 贡献。
//Author: RingweEH
int main()
{
n=read();
for ( int i=1; i<=n; i++ ) a[i]=read(),f[1<<(i-1)]=1;
int lim=1<<n; g[0]=1;
for ( int S=0; S<lim; S++ )
for ( int i=0; i<n; i++ )
if ( S&(1<<i) ) sum[S]+=a[i+1];
for ( int S=0; S<lim; S++ )
if ( sum[S]>=0 )
{ for ( int i=0; i<n; i++ ) if ( !(S&(1<<i)) ) bmod(f[S|(1<<i)],f[S]); }
else for ( int i=0; i<n; i++ ) if ( S&(1<<i) ) bmod(g[S],g[S^(1<<i)]);
ll ans=0;
for ( int S=0; S<lim; S++ ) bmod(ans,sum[S]*f[S]%Mod*g[(lim-1)^S]%Mod);
printf("%lld
",ans );
return 0;
}
真实排名
-
如果第 (i) 个人的成绩( (x) )不变,那么能变动的是:原本就比他高的,和比他小且变了之后还是比他小的。剩下的一个不能动。
-
如果第 (i) 个人的成绩变动,(xleq a_j< 2x) 都必须要两倍。这些是强制要变,剩下可以随便动。
对于第一种情况,不能动的区间是连续的一段;对于第二种,可以随意动的是一个连续区间,因此可以排序后双指针,(mathcal{O}(nlog n)) .
写挂了 /wq
理解了一发 @zkdxl
的代码 Orz 完整代码
之前有一发忘记判组合数负数结果上洛谷就 ub 了 /kk
int main()
{
n=read(); k=read(); C_Init(N-10);
for ( int i=1; i<=n; i++ ) a[i].val=read(),a[i].id=i;;
sort(a+1,a+1+n); //从大到小
int las=1;
for ( int i=2; i<=n; i++ )
if ( a[i].val!=a[i-1].val )
{
for ( int j=las; j<i; j++ ) bnd[j]=i-1;
las=i;
}
for ( int j=las; j<=n; j++ ) bnd[j]=n;
for ( int i=n,le=n; i; i-- )
{
while ( a[i].val>a[le].val*2 && le ) le--;
nxt[i]=le+1;
}
for ( int i=1,ri=1; i<=n; i++ )
{
while ( a[i].val*2<=a[ri].val && ri<=n ) ri++;
pre[i]=ri-1;
}
for ( int i=1; i<=n; i++ )
if ( (i>1) && a[i].val==a[i-1].val ) ans[a[i].id]=ans[a[i-1].id];
else if ( a[i].val==0 ) ans[a[i].id]=C(n,k);
else
{
int x=bnd[i]-i,y=i-pre[i],z=nxt[i]-i;
//x: count(a[j]=a[i])-1
//y: a[i]*2>a[j]
//z: a[j]*2>=a[i]
bmod(ans[a[i].id],C(n-z+x,k));
bmod(ans[a[i].id],C(n-y-x,k-y-x));
}
for ( int i=1; i<=n; i++ ) printf("%d
",ans[i] );
return 0;
}
神仙的游戏
首先一个很显然的事情是,如果存在一个长度为 (x) 的 border ,总长度为 (n) ,那么 (n-x) 一定是一个合法的循环节长度(不一定刚好整除)。进而有:如果 (iequiv j pmod{n-x}) 且 (s[i],s[j]) 分别是 (0,1) ,那么 (x) 显然不是合法的 border 。
也就是说,原串的 ?
其实没用,所有的 01
都是一种限制。一个暴力的做法是直接枚举原串所有的位置对得出 01
的限制,进而求出合法的 border 。
考虑优化。令 (displaystyle F(x)=sum_{i=0}^{n-1}[s_i=0]x^i,G(x)=sum_{i=0}^{n-1}[s_i=1]x^{-i}) ,那么 (f(i)) 的生成函数就是 (F(x)*G(x)) . 由于 (G) 的幂次全是负数,所以整体平移即可。完整代码 (最优解第一页 /se
)
//Author: RingweEH
using Poly::Poly_Mul;
int n,F[M],G[M];
char s[N];
int main()
{
scanf("%s",s); n=strlen(s);
for ( int i=0; i<n; i++ ) F[i]=(s[i]=='0'),G[i]=(s[n-1-i]=='1');
Poly_Mul(n,n,F,G,F);
ll ans=1ll*n*n;
for ( int i=1; i<n; i++ )
{
bool fl=0;
for ( int j=i; j<n; j+=i ) fl|=(F[n-1+j]+F[n-1-j]>0);
if ( !fl ) ans^=(1ll*(n-i)*(n-i));
}
printf("%lld
",ans );
return 0;
}
星际穿越
没看到 (l<r<x) . /fn
要计算 (dfrac1{r_i-l_i+1}displaystylesum_{y=l_i}^{r_i} dis(x_i,y)) ,一个 (O(n^2)) 的做法是预处理出两两距离,然后做一遍前缀和直接相减。设 (f[i][j]) 是以 (i) 为起点,走 (j) 步能到达的左端点(注意有一次向右的可能性),每次用 (kge f[i][j-1]) 的 (l[k]) 更新 (f[i][j+1]) ,并且 (dis[i][k]=j) ,直到 (f[i][j-1]=1) 。这样能有 (70pts) ,代码如下:
mn[n+1]=n+1;
for ( int i=n; i; i-- ) mn[i]=min(mn[i+1],l[i]);
for ( int i=1; i<=n; i++ ) f[i][0]=i,f[i][1]=l[i],f[i][2]=mn[i+1];
for ( int i=2,k,j; i<=n; i++ )
{
k=i-1;
for ( j=1; f[i][j]>1; j++ )
for ( ; k>=f[i][j]; k-- )
bmin(f[i][j+1],l[k]),dis[i][k]=j;
for ( ; k; k-- ) dis[i][k]=j;
}
然后倍增优化。设 (f[i][j]) 表示 ([i,n]) 内节点走 (2^j) 步能到达的左端点,有
再维护一个 (g[i][j]) 表示 (i) 到 ([f[i][j],i-1]) 内所有点的距离和。那么
好高啊 /ll
连倍增都不会了 /wq
完整代码
//Author: RingweEH
void Init()
{
lg=log2(n)+1; powe[0]=1;
for ( int i=1; i<=lg; i++ ) powe[i]=powe[i-1]*2;
f[n][0]=l[n];
for ( int i=n-1; i>=1; i-- )
f[i][0]=min(f[i+1][0],l[i]),g[i][0]=i-f[i][0];
for ( int j=1; j<=lg; j++ )
for ( int i=1; i<=n; i++ )
if ( f[i][j-1] )
{
f[i][j]=f[f[i][j-1]][j-1];
g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]+(f[i][j-1]-f[i][j])*powe[j-1];
}
}
int Calc( int x,int le ) //answer in [l,x-1]
{
if ( l[x]<=le ) return x-le;
int res=x-l[x],nw=1; x=l[x];
for ( int i=lg; i>=0; i-- )
if ( f[x][i]>=le )
{
res+=g[x][i]+nw*(x-f[x][i]);
nw+=(1<<i); x=f[x][i];
}
if ( x>le ) res+=x-le+nw*(x-le);
return res;
}