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; }