2020hdu多校第五场比赛及补题

1009 Paperfolding

把一张矩形纸折n次,每次折可以从上往下折,或从下往上折,或从左往右折,或从右往左折

这样一共有4^n种折法,折好后再把纸横切一刀,竖切一刀,再把纸展开,问展开后期望是多少张纸

看起来好难的一题,比赛刚开始的时候,我还没读完题时,旁边的人都开始折纸了,我想:这题真的要动手折纸吗?然而我开始分析的时候,就忍不住开始折纸了2333

分析出来后就不难了

分析:

  • 从上往下折等价于从下往上折,从左往右折等价于从右往左折,自己拿张纸折几下,靠直觉得出这个结论
  • 自己折折纸,可以发现从左往右折a次,从上往下折b次,最后展开是((1<<a)+1)*((1<<b)+1)张纸(我找的规律,没去证明,就感觉是这样)

计算:

一共2^n种折法,分母就是2^n,

从左往右一共折a次,从上往下一共折n-a次的情况是C(n,a)种(我还不会打数学符号啊啊啊),贡献为C(n,a)*((1<<a)+1)*((1<<(n-a))+1)

检验一下,a的范围是0到n,C(n,0)+C(n,1)+C(n,2)+...+C(n,n)= 2^n种

分子就是把C(n,a)*((1<<a)+1)*((1<<(n-a))+1)累加起来(a从0到n)

C(n,a)*((1<<a)+1)*((1<<(n-a))+1) =       C(n,a) * [  (1<<n) + (1<<a) + (1<<(n-a)) + 1] = [C(n,a)*(1<<n)] + [C(n,a)*(1<<a)] + [C(n,a)*(1<<(n-a))] + [C(n,a)]

[C(n,a)*(1<<n)] 累加起来等于 4^n,除以分母后等于 2^n

[C(n,a)*(1<<a)]累加起来等于(2+1)^n = 3^n,除以分母后等于 3^n * inv(2^n)

[C(n,a)*(1<<(n-a))]累加起来,根据对称性,它等于[C(n,a)*(1<<a)]

[C(n,a)]累加起来等于2^n ,除以分母后等于1

#include<iostream>
#include<algorithm>
using namespace std;
const long long MOD = 998244353;
long long qpow(long long n, long long p) {
    long long ret = 1;
    for (; p; p >>= 1, n = n * n % MOD)
        if (p & 1)
            ret = ret * n % MOD;
    return ret;
}
long long inv(long long a) {
    return qpow(a, MOD - 2);
}
int main()
{
    int T;
    long long n;
    cin>>T;
    while(T--){
        scanf("%lld",&n);
        long long res, tt, ni;
        res = qpow(2,n);
        tt = 2*qpow(3,n);
        ni = inv(res);
        tt = tt * ni % MOD;
        res += tt + 1;
        res %= MOD;
        printf("%lld\n",res);
    }
    return 0;
}

  

1001 Tetrahedron

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 6e6 + 7;
const int MOD = 998244353;
// const int INF = ;
// const int DIRX[] = {};
// const int DIRY[] = {};

int ept[MAXN];

inline int getpow(int a, int b)
{
    int ret = 1;
    for (; b; b >>= 1, a = a * a % MOD)
        if (b & 1)
            ret = ret * a % MOD;
    return ret;
}

void pre()
{
    int tmp = 0;
    for (int i = 1; i <= 6e6; ++i)
    {
        tmp += getpow(i * i % MOD, MOD - 2);
        tmp %= MOD;
        ept[i] = tmp;
    }
}

int T;
int n;
int ans;

int32_t main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    pre();
    cin >> T;
    while (T--)
    {
        cin >> n;
        ans = (3 * ept[n] % MOD) * getpow(n, MOD - 2) % MOD;
        cout << ans << endl;
    }
    return 0;
}

  

1003 Boring Game

很恶心的一题,折叠3次的情况我根本画不出,这题耗了很长时间,差不多3小时的时候,大家都A不出题,这时突然有个队大叫A了!A了!然后我马上看下榜单,并没有什么变化,学长问他们哪题A了,他们说样例A了...xswl

关键是模拟构造order数组,折2次及以上是有构造规律的,折1次的情况是特判

#include<iostream>
#include<algorithm>
#include<cstdio> 
using namespace std;
int zheng[203][2050];
int fan[203][2050];
int order[2050],neworder[2050];
int main()
{
	int T,n,k,cnt;
	cin>>T;
	while(T--){
		cin>>n>>k;
		cnt = 0;
		for(int i = (1<<k);i;i--){
			order[i] = 0;
			cnt++;
			if(i % 2) {//正 
				for(int j = 1;j <= n;j++){
					scanf("%d%d",&zheng[j][cnt],&fan[j][cnt]);
				}
			}
			else{//反 
				for(int j = n;j ;j--){
					scanf("%d%d",&fan[j][cnt],&zheng[j][cnt]);
				}
			}
		}
		int len = 2;
		order[1] = 1 ;order[2] = 2;//折2次的情况把折1次的情况包括进去了,初始化就直接初始成折2次的情况 
		for(int e = 2;e <= k;e++){//从折2次开始构造 
			for(int i = 1;i<=len;i++){
				int x = order[len-i+1];
				if(x%2) neworder[i] = x * 2;
				else neworder[i] = x * 2 - 1; 
			}
			for(int i = len+1;i<=len*2;i++){
				int x = order[i-len];
				if(x%2) neworder[i] = x * 2 - 1;
				else neworder[i] = x * 2;
			}
			len *= 2;
			for(int i = 1;i <= len;i++){
				order[i] = neworder[i];
				//cout<<order[i]<<" ";
			}
			//cout<<endl;
		}
		for(int i = 1;i<=n;i++){
			for(int j = 1;j <= (1<<k) ;j++){
				printf("%d ",zheng[i][order[j]]);
			}
			//printf("\n");
			for(int j = 1;j < (1<<k) ;j++){
				printf("%d ",fan[i][order[j]]);
			}
			printf("%d",fan[i][order[(1<<k)]]);
			if(i!=n) printf(" ");	
			//printf("\n");
		}
		printf("\n");
	}
	return 0;
}

  

PE了2发,惊了,末尾多输出空格会PE

1012 Set1

一个集合为1,2,3,4,...,n ,n是奇数,每次操作是集合中会删掉一个最小的数,然后随机选一个数删除,重复进行这集合个操作直到集合中只剩下最后一个数

求1,2,3....分别为最后一个数的概率

分母 = 总的操作方案数 = (n-1)*(n-3)*(n-5)*...*2 = ((n-1)/2)! * 2^((n-1)/2)

1,2,3,...(n-1)/2是不可能成为最后一个数的

第 i 个数,在其后 n - i 个数必须要删完,也就是分子 = (n-i)! * 某个东西

比赛的时候就是这个某个东西算不来

关键的一点就是,在删这n - i个数中的某个数a时,在前 i - 1个数中删掉了剩余的最小的数b,这n - i 个a对应n - i个b,而这n - i 个 b 可以是前 i - 1个数里选出的 n - i 个数的任意组合,情况有C(i - 1,n - i)种,这 n - i 个 b确定后,前 i - 1 个数里剩下的数就可以任意删了

假设剩下的数有 re 个,任意删的方案数 = (n-1)*(n-3)*(n-5)*...*1 = (n-1)! / [(n-2)*(n-4)*...*2] = (n-1)! / { [(n-2)/2]! * 2^[(n-2)/2] } 

某个东西 = C(i - 1,n - i) * (n - 1)! / { [(n-2)/2]! * 2^[(n-2)/2] } 

主要就是没想到后n - i 个数对应前 i - 1个数中 n - i个数

#include<iostream>
#include<algorithm> 
using namespace std;
const int MAXN = 5e6+7;
const long long MOD = 998244353; 
long long fact[MAXN];
long long inv[MAXN];
void pre(){
	fact[0] = 1;
	for(int i = 1;i <= 5e6;i++)
		fact[i] = fact[i-1] * i % MOD;
	inv[1] = 1;
    for (int i = 2; i <= 5e6; i++)
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    inv[0] = 1;
    for (int i = 1; i <= 5e6; i++)
        inv[i] = inv[i] * inv[i - 1] % MOD;
}

long long qpow(long long n, long long p) {
    long long ret = 1;
    for (; p; p >>= 1, n = n * n % MOD)
        if (p & 1)
            ret = ret * n % MOD;
    return ret;
}
long long mod_inv(long long a){
	return qpow(a,MOD-2);
}
long long C(int n,int m){
	return fact[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{
	int T, n;
	pre();
	cin>>T;
	long long MODINV2 = mod_inv(2);
	while(T--){
		cin>>n;
		if(n == 1) {
			printf("1\n");
			continue;
		}
		long long fm, invfm;
		fm = qpow(2,n/2)*fact[n/2]%MOD;
		invfm = mod_inv(fm);
		int pos = n / 2 + 1;
		int re = 0;
		long long tt = 1;
		for(int i = 1;i < pos;i++) printf("0 ");
		for(;pos <= n;pos++){
			long long res = fact[n-pos];
			res = res * C(pos-1,n-pos) % MOD;
			long long ret ;
			if(!re) ret = 1;
			else{
				ret = fact[re-1] * inv[(re-2)/2] % MOD * tt % MOD;
				tt = tt * MODINV2 % MOD;
			}
			res = res * ret % MOD * invfm % MOD;
			re += 2;
			if(pos < n)printf("%lld ",res);
			else printf("%lld\n",res);
		}
	}
	return 0;
} 

  

原文地址:https://www.cnblogs.com/ruanbaitql/p/13560700.html