PKUSC2018

最大前缀和

如果钦定某个位置为最大前缀和,设位置为 \(pos\) ,那么必然也是 \([1,pos]\) 的最大前缀,并且 \([pos+1,n]\) 的前缀和均 \(<=0\) .

\(n\leq 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\n",ans );

	return 0;
}

真实排名

  • 如果第 \(i\) 个人的成绩( \(x\) )不变,那么能变动的是:原本就比他高的,和比他小且变了之后还是比他小的。剩下的一个不能动。

  • 如果第 \(i\) 个人的成绩变动,\(x\leq a_j< 2x\) 都必须要两倍。这些是强制要变,剩下可以随便动。

对于第一种情况,不能动的区间是连续的一段;对于第二种,可以随意动的是一个连续区间,因此可以排序后双指针,\(\mathcal{O}(n\log 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\n",ans[i] );

	return 0;
}

神仙的游戏

首先一个很显然的事情是,如果存在一个长度为 \(x\) 的 border ,总长度为 \(n\) ,那么 \(n-x\) 一定是一个合法的循环节长度(不一定刚好整除)。进而有:如果 \(i\equiv 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\n",ans );

	return 0;
}

星际穿越

没看到 \(l<r<x\) . /fn

要计算 \(\dfrac1{r_i-l_i+1}\displaystyle\sum_{y=l_i}^{r_i} dis(x_i,y)\) ,一个 \(O(n^2)\) 的做法是预处理出两两距离,然后做一遍前缀和直接相减。设 \(f[i][j]\) 是以 \(i\) 为起点,走 \(j\) 步能到达的左端点(注意有一次向右的可能性),每次用 \(k\ge 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\) 步能到达的左端点,有

\[f[i][0]=\min(l_k),k\in [i,n]\\\\ f[i][j]=f[f[i][j-1]][j-1] \]

再维护一个 \(g[i][j]\) 表示 \(i\)\([f[i][j],i-1]\) 内所有点的距离和。那么

\[g[i][0]=i-f[i][0]\\\\ g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]+2^{j-1}(f[i][j-1]-f[i][j]) \]

好高啊 /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;
}
posted @ 2021-05-10 14:46  MontesquieuE  阅读(136)  评论(0编辑  收藏  举报