Codeforces#678 Div2

Contest

B - Prime Square

构造 \(n\times n\) 的矩阵使得每行每列都是素数且每个格子都不是。


设和为 \(p\) ,在对角线上放 \(p-n+1\) ,其余放 \(1\) ,找最小的一个 \(p\) 使得 \(p\in primes\)\(p-n+1\) 不是即可。

给你一个在有序序列中二分 \(x\) 得到的位置,问无序情况下有多少种 \(n\) 的排列 “二分” \(x\) 得到的位置不变。


模拟二分过程,统计到 \(pos\) 需要 “遇到” 多少个比 \(x\) 小的,和多少个比 \(x\) 大的,剩下的位置就是随意填,然后排列组合即可。

//Author: RingweEH
const int Mod=1e9+7;
int n,x,pos;

int main()
{
	n=read(); x=read(); pos=read();
	int l=0,r=n,lar=0,sma=0;
	while ( l<r )
	{
		int mid=(l+r)>>1;
		if ( mid==pos ) l=mid+1;
		else if ( mid>pos ) r=mid,lar++;
		else l=mid+1,sma++;
	}
	
	int ans=1;
	if ( sma>=x || lar>n-x ) { puts( "0" ); return 0; }
	for ( int i=x-1,j=1; j<=sma; i--,j++ ) ans=1ll*ans*i%Mod;
	for ( int i=n-x,j=1; j<=lar; i--,j++ ) ans=1ll*ans*i%Mod;
	for ( int i=1; i<=n-sma-lar-1; i++ ) ans=1ll*ans*i%Mod;
	printf( "%d\n",ans );

	return 0;
}

D - Bandit in a City

转化后的题意:给定以 \(1\) 为根的 \(n\) 个节点的一棵树,每个节点上有 \(a_i\) 个人,每个人可以选择往任意子节点走,直到走到叶子节点为止,问最后人最多的叶子节点最少有多少人。


DFS 处理出子树中叶子结点个数 \(cnt\) 和人数总和 \(sum\) ,答案就是 \(\max\{\left\lceil\dfrac{sum}{cnt}\right\rceil\}\) .

//Author: RingweEH
void DFS( int u )
{
	sum[u]=a[u];
	for ( auto v : g[u] )
		DFS(v),cnt[u]+=cnt[v],sum[u]+=sum[v];
	if ( !g[u].size() ) cnt[u]=1;
	ans=max( ans,(sum[u]+cnt[u]-1)/cnt[u]);
}

E - Complicated Computations

求一个数列的所有连续子数列的 \(\tt mex\) 值的 \(\tt mex\)


离谱题面 如果所有连续子序列的 \(\tt mex\) 集合中存在 \(x\) ,那么一定有一个子序列满足:\(1\sim x-1\) 均出现,且没有 \(x\)

注意到值域是 \(1e5\) ,所以考虑枚举答案,那么需要做到 \(\log\) 查询是否有区间符合条件。

设当前枚举的数为 \(x\) ,按照 \(x\) 在序列中的所有出现位置分段(和头尾、两个 \(x\) 之间),问题转化为对于每一段询问是否出现了 \(1\sim x-1\) . 设当前区间为 \([l,r]\) ,问是否出现 \(a\) ,就是在查询 \([1,r]\) 之间 \(a\) 的最后出现位置是否 \(>l\) ,维护 \(n\) 棵线段树即可。

直接做显然会炸……考虑把所有东西挂到一棵权值线段树上,叶子结点的意义(设代表了数 \(x\) )就是数 \(x\) 目前在序列中最后出现的位置,维护区间最小值。设当前遍历到区间 \([1,pos]\)\(las[a[pos]]\) 表示数 \(a[pos]\) 上一次出现的位置,直接查询 \(1\sim a[pos]-1\) 在线段树上的最小值,也就是 \(1\sim a[pos]-1\) 最后出现位置的最小值 \(num\) ,如果 \(num>las[a[pos]]\) 那么就符合条件。

注意最后还要统计一次所有 \(a[i]\) 到结尾的区间。

//Author: RingweEH
#define lc pos<<1
#define rc pos<<1|1
const int N=1e5+10,INF=1e6+10;
int las[N],tr[N<<2],n,a[N];
bool vis[N];

void Pushup( int pos ) { tr[pos]=min(tr[pos<<1],tr[pos<<1|1]); }
void Modify( int pos,int l,int r,int x,int val )
{
	if ( x>r || x<l ) return;
	if ( l==r ) { tr[pos]=val; return; }
	int mid=(l+r)>>1;
	Modify( lc,l,mid,x,val ); Modify( rc,mid+1,r,x,val );
	Pushup( pos );
}
int Query( int pos,int L,int R,int l,int r )
{
	if ( L>r || R<l ) return INF;
	if ( l<=L && R<=r ) return tr[pos];
	int mid=(L+R)>>1;
	int res=Query(lc,L,mid,l,r); bmin( res,Query(rc,mid+1,R,l,r) );
	return res;
}

int main()
{
	n=read();
	for ( int i=1,x; i<=n; i++ )
	{
		a[i]=read(); x=0;
		if ( a[i]>1 ) x=Query(1,1,n,1,a[i]-1);
		if ( x>las[a[i]] ) vis[a[i]]=1;
		Modify(1,1,n,a[i],i); las[a[i]]=i;
	}
	for ( int i=2,x; i<=n+1; i++ )
	{
		x=Query(1,1,n,1,i-1); 
		if ( x>las[i] ) vis[i]=1;
	}
	for ( int i=1; i<=n; i++ )
		if ( a[i]^1 ) vis[1]=1;
	
	int p=1;
	while ( 1 )
	{
		if ( !vis[p] ) { printf( "%d\n",p ); return 0; }
		p++;
	}

	return 0;
}

F - Sum Over Subsets

求可重集 \(S\) 的两个子集 \(A,B\) 满足:

  • \(B\)\(A\) 的子集
  • \(B\) 元素个数比 \(A\) 少一
  • \(A\) 所有元素的 \(\gcd=1\)

求所有 \(\sum\limits_{x\in A}*\sum\limits_{x\in B}\bmod 998244353\) 之和,值相等的不同的元素算不同集合。


很有意思的题。\(\gcd\) 和倍数相关,那么就统计 \(g[i]\) 表示 \(\gcd\)\(i\) 的倍数的答案,然后莫反变成 \(f[i]\) 表示 \(\gcd=i\) 的答案。而我们所求是 \(f[1]\) ,也就是说只需要累加所有的 \(g(x)\mu(x)\) 就好了.

对于 \(g\) ,贡献有两种:( \(k\) 表示总个数)

  • \(a[i]\cdot a[i]\) ,方案数有 \(2^{k-2}\cdot (k-1)\)
  • \(a[j]\cdot a[i]\) ,方案数为 \(2^{k-3}\cdot (k-2)+2^{k-2}\) ,然后再算上两个集合互换的情况。
//Author: RingweEH
int main()
{
	n=read();
	for ( int i=1,x; i<=n; i++ )
		x=read(),a[x]=read();
	
	mu[1]=1;
	for ( int i=1; i<=N-10; i++ )
		for ( int j=i+i; j<=N-10; j+=i ) 
			mu[j]-=mu[i];
	ll ans=0,cnt,sum,sumq;
	for ( int i=1; i<=N-10; i++ )
	{
		cnt=0,sum=0,sumq=0;
		for ( int j=i; j<=N-10; j+=i )
		{
			cnt+=a[j]; (sum+=a[j]*j%Mod)%=Mod;
			(sumq+=a[j]*j%Mod*j%Mod)%=Mod;
		}
		if ( cnt<2 ) continue;
		ll t1=sumq*power(2,cnt-2)%Mod*((cnt-1)%Mod)%Mod;
		ll t2=(sum*sum%Mod+Mod-sumq)%Mod;
		ll res=power(2,cnt-2);
		if ( cnt>2 ) (res+=((cnt-2)%Mod)*power(2,(cnt-3)%(Mod-1))%Mod)%Mod;
		res=res*t2%Mod; (res+=t1)%=Mod;
		ans=(ans+res*mu[i]%Mod+Mod)%Mod;
	}
	
	printf( "%lld\n",ans );

	return 0;
}
posted @ 2021-02-08 14:51  MontesquieuE  阅读(78)  评论(0)    收藏  举报