zr2019暑期高端峰会AB组day8

zr2019暑期高端峰会AB组day8

dls的比赛真的真的好duliu啊

A. 抽象代数题(构造题)

link

  • A = 100 A=100 A=100时,每个序列给它一个唯一的置换
  • A = 50 A=50 A=50时,由于 n = 50 n=50 n=50,我们可以构造 49 49 49个置换,每个置换让第一个位置和第 i i i个位置交换,其他位置不动,那么交换两个数需要三次操作,总操作数最多 3 ∗ ( n − 1 ) 3*(n-1) 3(n1)
  • A = 30 A=30 A=30时,我们对前 25 25 25个元素构造和 1 1 1的置换,外加一个前 25 25 25位和后 25 25 25位交换位置的置换,那么我们虽然不能直接让所有元素和位置 1 1 1交换,但是由于我们分了两个,它们可以轮流得到和 1 1 1交换的机会,这样我们也能得到所有的排列,同样地,组数分得不一样也能得到相应的部分分
  • 我们思考 A A A至少要是几我们才可以保证可以得到所有的排列,其实 A = 2 A=2 A=2就可以了,我们构造一个位置 1 1 1和位置 2 2 2的交换,和一个 2 − n 2-n 2n的循环移位,那么对于每一对需要交换的位置,都可以用循环移位的操作放到 1 1 1 2 2 2的位置交换
  • A = 5 A=5 A=5时,我们仿照上面的思路,减少步数的使用,上面的方法慢就慢在循环移位必须移位移位地移动,那么怎么样才能大步大步地移位且能使每一位都能到位置 2 2 2呢?倍增!但是 2 3 = 8 2^3=8 23=8还不够爽快,那么就用三进制, 1 , 3 , 9 , 27 1,3,9,27 13927
  • 具体的操作方法: 1 , 1, 1 若位置 1 1 1上的数不是 1 1 1,把它该在的位置(和 2 2 2的相对位置)移到 1 1 1旁边和它交换, 2 , 2, 2若1的位置上时 1 1 1,找到 2 2 2后面第一个不应该在那的数,把它放到位置 1 1 1上重复步骤 1 1 1

B. 数据结构题(计数题)

link

这题题面简洁明了,看似清新,实际是一个大力推式子的题,区分了我这种不会推式子的人,首先写一写方差的公式,我们可以把静态序列的方差写成这样
∑ i = 1 n a i 2 n − ( ∑ a i ) 2 n 2 = ∑ a i 2 n − 1 n 2 ∑ i = 1 n ∑ j = 1 n a i a j \frac{\sum_{i=1}^n a_i^2}{n}-\frac{(\sum a_i)^2}{n^2} = \frac{\sum a_i^2}{n}-\frac{1}{n^2}\sum_{i=1}^n \sum_{j=1}^na_ia_j ni=1nai2n2(ai)2=nai2n21i=1nj=1naiaj

把后面那一项, i = j i=j i=j i ≠ j i \ne j i̸=j分开算贡献
∑ i = 1 n a i 2 n − 1 n 2 ∑ i = 1 n a i 2 − ∑ i ∑ j [ i ≠ j ] a i a j \frac{\sum_{i=1}^n a_i^2}{n}-\frac{1}{n^2}\sum_{i=1}^na_i^2-\sum_{i}\sum_j[i \ne j]a_ia_j ni=1nai2n21i=1nai2ij[i̸=j]aiaj

我们现在考虑算序列的所有子集的方差和,先枚举一个 k k k代表子集大小,那么大小为 k k k的子集的贡献为
( n − 1 k − 1 ) k ∑ i = 1 n a i 2 − ( n − 1 k − 1 ) k 2 ∑ i = 1 n a i 2 − ( n − 2 k − 2 ) k 2 ∑ i ≠ j a i a j \frac{\tbinom{n-1}{k-1}}{k}\sum_{i=1}^na_i^2-\frac{\tbinom{n-1}{k-1}}{k^2}\sum_{i=1}^na_i^2-\frac{\tbinom{n-2}{k-2}}{k^2}\sum_{i \ne j}a_ia_j k(k1n1)i=1nai2k2(k1n1)i=1nai2k2(k2n2)i̸=jaiaj

我们有这三项东西,每一项的右边和 a a a有关的我们都能用线段树轻松维护,至于前面的组合数系数,就又是轮到了推式子的时刻

  • ( n − 1 k − 1 ) k \frac{\tbinom{n-1}{k-1}}{k} k(k1n1)
    这一项是最简单的,我们只用一个配凑组合数的技巧就能搞定,数学竞赛也经常用
    ( n − 1 k − 1 ) k = 1 k ( n − 1 ) ! ( k − 1 ) ! ( n − k ) ! = 1 n n ! k ! ( n − k ) ! = ( n k ) n \frac{\tbinom{n-1}{k-1}}{k}=\frac{1}{k}\frac{(n-1)!}{(k-1)!(n-k)!}=\frac{1}{n}\frac{n!}{k!(n-k)!}=\frac{\tbinom{n}{k}}{n} k(k1n1)=k1(k1)!(nk)!(n1)!=n1k!(nk)!n!=n(kn)

  • ( n − 1 k − 1 ) k 2 \frac{\tbinom{n-1}{k-1}}{k^2} k2(k1n1) 差分。。。

有点晚了,未完待续。。。

C. 组合计数题

link

  • 先按右端点排序
  • 对于每个区间求出它最左边能碰到的一个区间,和最右边能碰到的一个区间
  • 然而我们只考虑这两个边界就行了,问题变成了,一些区间,其中选一些出来使得并集是全集,这个dp可以用线段树维护
T1赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b);i--)
using namespace std;
const int N=300;
int n,A,B,C,T,a[N][N],ans[N][N],tag[N];
int Ans[N][N],t[6]={0,0,1,3,8,20};
inline int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
inline int Get(int x)
{
	if (x<2) return Get(n-(2-x)+1);
	if (x>n) return Get(x-n+1);
	return x;
}
inline void Input()
{
	n=read(),A=read(),B=read(),C=read();
	T=read();
	FOR(i,1,T) FOR(j,1,n) a[i][j]=read();
	ans[1][2]=1;ans[1][1]=2;FOR(i,3,n) ans[1][i]=i;
	ans[2][1]=1;FOR(i,2,n) ans[2][i]=Get(i+1);
	ans[3][1]=1;FOR(i,2,n) ans[3][i]=Get(i+3);
	ans[4][1]=1;FOR(i,2,n) ans[4][i]=Get(i+8);
	ans[5][1]=1;FOR(i,2,n) ans[5][i]=Get(i+20);
	return;
}
inline void Turn(int p,int step)
{
	int b[N];
	b[1]=a[p][1];
	FOR(i,2,n) b[i]=a[p][Get(i+step)];
	FOR(i,1,n) a[p][i]=b[i];
	For(i,5,2) while (step>=t[i]) step-=t[i],Ans[p][++Ans[p][0]]=i;
	return;
}
inline void SWAP(int p)
{
	swap(a[p][1],a[p][2]);
	Ans[p][++Ans[p][0]]=1;
	return;
}
inline void Solve(int p)
{
	int pos2,ok;
	FOR(i,1,n) tag[i]=0;
	tag[2]=1;
	if (a[p][1]==2) SWAP(p);
	FOR(i,2,n) if (a[p][i]==2) pos2=i;
	FOR(i,2,n)
	{
		int check=i-pos2;
		if (check<0) check=n-2-(pos2-i)+1;
		if (a[p][i]-2==check) tag[a[p][i]]=1;
	}
	while (1)
	{
		
		FOR(i,2,n) if (a[p][i]==2) pos2=i;
		if (a[p][1]!=1)
		{
			int endpos=Get(pos2+a[p][1]-2);
			tag[a[p][1]]=1;
			Turn(p,endpos-2);
			SWAP(p);
		}
		else
		{
			int tmp,ok=1;
			FOR(i,2,n) 
			{
				int check=i-pos2;
				if (check<0) check=n-2-(pos2-i)+1;
				if (a[p][i]-2!=check) 
				{
					tag[a[p][i]]=1;
					ok=0;
					tmp=i;
				}
			}
			if (!ok)
			{
				Turn(p,tmp-2);
				SWAP(p);
			}
			else
			{
				Turn(p,pos2-2);
				break;
			}
		}
	}
	if (!Ans[p][0]) Ans[p][++Ans[p][0]]=1,Ans[p][++Ans[p][0]]=1;
	return;
}
inline void Output()
{
	FOR(i,1,5)
	{
		FOR(j,1,n) printf("%d ",ans[i][j]);
		printf("\n");
	}
	FOR(i,6,A)
	{
		FOR(j,1,n) printf("%d ",j);
		printf("\n");
	}
	FOR(i,1,T)
	{
		printf("%d ",Ans[i][0]);
		FOR(j,1,Ans[i][0]) printf("%d ",Ans[i][j]);
		printf("\n");
	}
	return;
}
int main()
{
	Input();
	FOR(i,1,T) 
		Solve(i);
	Output();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值