E 弦
显然我们需要给出所有弦不交的概率P。
先对分子(两两相连且不相交的情况总数)进行推导:
其实这和经典题目凸多边形的三角形划分很相似(但我也没有做过)。
易知 时有1种情况,
有2种情况。
时有五种情况,如图:
其实这里已经可以大概猜到是卡特兰数了,但是这里我们做一些更严谨的推导,网上的推导要么就是没有,要么就复杂而且不讲人话。下面给出一种简洁的证法来证明连不相交弦的情况总数为卡特兰数。
众所周知,卡特兰数本身和栈/递归联系非常紧密。所以无论是理解还是证明卡特兰数,引入递归的思想十分重要。
比如对于n=4的情况,我们轻轻地连一条线。此时圆被我们切割成了两个部分:线的左边和右边。左边和右边都是同样的问题,也就是同质子问题。在这里,线的左边转换成了n=1的情况,线的右边转化成了n=2的情况。不断地分割,直到不可分割(n=0),也是递归的出口。
依次连接1-2,1-4,1-6,1-8,这四种情况就对应着四个分式:
所以数学归纳法推广到
即分子为卡特兰数。故分子满足
下面计算分母。
一开始在个点里随便选两个,然后在
个点里随便选两个……然后在最后剩下的两个点里面选2个,于是我们就得到了
。
但是!算重了!
以n=2举例
分别对应
1-2 3-4 1-3 2-4 1-4 2-3 2-3 1-4 2-4 1-3 3-4 1-2
可以发现算重了一遍,但去重并不是/=2
以n=3举例 ,第一轮重复数量/=1,第二轮重复数量/=2,第三轮重复数量/=3.
在上面的计算方式中,每次选取选了两个点确定了一条边,但是这n个边并没有定顺序,所以除以一个全排列
所以我们得到了下面的公式:
化简得到:
然后就很简单了,快速幂+费马小定理得逆元。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; ll qkpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } ll getInv(ll a) { return qkpow(a, mod - 2); } //求一个数的逆元 int main() { ll n; cin >> n; ll ans = 1; for (int i = 2; i <= n + 1; ++i) ans *= i, ans %= mod; ans = qkpow(2, n) * getInv(ans) % mod; cout << ans << endl; return 0; }
F 排列计算
如果通过僵硬地涂色来计算单点权重,2e5*2e5必然TLE。
差分+前缀和可以完美地解决这个问腿。此处感谢钟涛大佬。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=200005; ll num[N]; int main() { ll n, m ; cin >> n >> m; while (m--) { ll l , r ; cin >> l >> r; num[l]++; num[r + 1]--; } for (int i = 2; i <= n; ++i) num[i] += num[i - 1]; sort(num + 1, num + 1 + n); ll ans = 0; for (int i = 1; i <= n; ++i) ans += i * num[i]; cout << ans << endl; return 0; }
A 游戏
因为是两个人,而且最后一定会把能拿的数全部拿完。所以我们只需要讨论能拿的数sum有多少个即可。
- 如果a,b两个数不互质,即他们的最大公因数g大于1,那么在
范围内,所有满足的
的数都会被拿走。
- 如果a,b两个数互质,即他们的最大公因数g等于1,那么在
范围内,所有的数都会被拿走。
所以我们知道sum=n/gcd(a,b),因为只有两人,且谁完成最后一次操作时,谁获胜。所以sum偶数时张老师必输,奇数时张老师必胜。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 7; const ll mod = 1e9 + 7; inline ll read() { ll s = 0, f = 1; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < 48 || ch > 57); while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * f; } int main() { ll t = read(); while (t--) { ll n = read(), a = read(), b = read(); n /= __gcd(a, b); if (n & 1) puts("Yes"); else puts("No"); } return 0; }
B 伤害计算
水题,因为不想写字符串处理所以用py十行搞定:
s=list(input().split('+')) ans=0.0 for i in s: flag=0 for j in i: if j=='d': flag=1 break if flag: a,b=map(int,i.split('d')) ans+=(1+b)*a*0.5 else: ans+=int(i) if ans-int(ans)>0.1: print(ans) else : ans=int(ans) print(ans)
D 车辆调度
因为数据很小,我们都意识到了暴力搜索,DFS可以更简单地解决。
这题的主要难点在于编码难度,我在写这道题的时候认为每搜索一次重新开辟一个二维数组太蠢,又没想清楚如何动态管理车辆的位置信息,其实不需要管理车辆信息,每次重新搜索就可以了。
#include <bits/stdc++.h> using namespace std; const int N = 15; const int inf = 0x3f3f3f3f; char mp[N][N]; int dx[] = {0, 0, -1, 1};//合起来分别对应上下左右 int dy[] = {1, -1, 0, 0}; int flag, n, m, k; bool check(int x, int y) { //找合法位置,在区域内且不可遇到障碍物 return x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] != 'R' && mp[x][y] != 'X'; } void dfs(int cnt) { if (cnt >= k) return; if (flag) return; for (int i = 1; i <= n; i++) //这样搜索就不用管理坐标了 for (int j = 1; j <= m; j++) if (mp[i][j] == 'R') //枚举每一辆车 for (int k = 0; k < 4; k++) { //枚举每一个方向 int tx = i, ty = j; while (check(tx + dx[k], ty + dy[k])) tx += dx[k], ty += dy[k]; //不断向前 if (mp[tx][ty] == 'D') flag = 1; //如果抵达 设为完成 swap(mp[tx][ty], mp[i][j]); //换过去 dfs(cnt + 1); //向下搜索 swap(mp[tx][ty], mp[i][j]); //换回来 } } int main() { cin >> m >> n >> k; for (int i = 1; i <= n; i++) scanf("%s", mp[i] + 1); dfs(0); puts(flag ? "YES" : "NO"); return 0; }
改了一下我比赛的时候写的
#include <bits/stdc++.h> using namespace std; typedef long long ll; char s[15][15]; bool finish = 0; int w, h, ti, a, b; void turnright(int x, int y) { int i; for (i = y + 1; i <= w; ++i) if (s[x][i] != '.' && s[x][i] != 'D') break; if (s[x][i - 1] == 'D') finish = 1; a = x, b = i - 1; } void turnleft(int x, int y) { int i; for (i = y - 1; i > 0; --i) if (s[x][i] != '.' && s[x][i] != 'D') break; if (s[x][i + 1] == 'D') finish = 1; a = x, b = i + 1; } void turndown(int x, int y) { int i; for (i = x + 1; i <= h; ++i) if (s[i][y] != '.' && s[i][y] != 'D') break; if (s[i - 1][y] == 'D') finish = 1; a = i - 1, b = y; } void turnup(int x, int y) { int i; for (i = x - 1; i; --i) if (s[i][y] != '.' && s[i][y] != 'D') break; if (s[i + 1][y] == 'D') finish = 1; a = i + 1, b = y; } void dfs(int times) { if (finish) return; if (times > ti) return; for (int i = 1; i <= h; ++i) for (int j = 1; j <= w; ++j) if (s[i][j] == 'R') { int xi, yi; turnup(i, j), swap(s[i][j], s[a][b]), xi = a, yi = b; dfs(times + 1), swap(s[i][j], s[xi][yi]); if (finish) return; turndown(i, j), swap(s[i][j], s[a][b]), xi = a, yi = b; dfs(times + 1), swap(s[i][j], s[xi][yi]); if (finish) return; turnleft(i, j), swap(s[i][j], s[a][b]), xi = a, yi = b; dfs(times + 1), swap(s[i][j], s[xi][yi]); if (finish) return; turnright(i, j), swap(s[i][j], s[a][b]), xi = a, yi = b; dfs(times + 1), swap(s[i][j], s[xi][yi]); if (finish) return; } } int main() { scanf("%d%d%d", &w, &h, &ti); for (int i = 1; i <= h; ++i) scanf("%s", s[i] + 1); dfs(1); puts(finish ? "YES" : "NO"); return 0; }
C 旅行
题意
n个点在数轴上排列,从其中一个点出发。每个点都有要求的最晚到达时间,问能否全部准时到达,如果能,给出完成时间。
分析
题目给出的n个景点是按照位置信息升序排列的。
把起始点,即t为0的点设为k,所有的点分成了两部分:k点左边的点和k点右边的点。
我们用一个数组dp[i][j][f]表示完成左边i个点右边j个点的最少所需时间,f==0时表示在左边结束,f==1时表示在右边结束。
看着数轴,考虑在左边结束的情况,即dp[i][j][0]:
- 上一个落脚点是左边(从右往左数,下同)第i-1个点,那么就是dp[i-1][j][0]再加上两点之间所需要消耗的时间即可。
- 上一个落脚点是右边第j个点.
所以
同理
然后逐个比对,遇到不符合的情况直接退出即可。
solution
#include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e3 + 5; int dp[N][N][2]; int d[N], t[N]; int main() { int n,k; cin >> n; for (int i = 1; i <= n; i++) cin >> d[i]; for (int i = 1; i <= n; i++) cin >> t[i]; for (int i = 1; i <= n; i++) if (t[i] == 0) k = i; fill(**dp, **dp+sizeof(dp)/4, INF); dp[0][0][1] = dp[0][0][0] = 0; for (int i = 0; i <= k - 1; i++) for (int j = 0; j <= n - k; j++) { if (i) dp[i][j][0] = min(dp[i - 1][j][0] - d[k - i] + d[k - i + 1], dp[i - 1][j][1] + d[j + k] - d[k - i]); if (j) dp[i][j][1] = min(dp[i][j - 1][1] + d[j + k] - d[j - 1 + k], dp[i][j - 1][0] + d[j + k] - d[k - i]); if (dp[i][j][0] > t[k - i] && dp[i][j][1] > t[j + k]) { cout << -1; return 0; } if (dp[i][j][0] > t[k - i]) dp[i][j][0] = INF; if (dp[i][j][1] > t[k + j]) dp[i][j][1] = INF; } cout << min(dp[k - 1][n - k][0], dp[k - 1][n - k][1]); return 0; }
因为这是我第一次写区间DP,所以犯了很多沙雕错误。
- 区间DP的起始点是
for (int i = 0; i <= x; i++)
这样的。 - 再比如这样的求最小,是把所有的不合法位置初始化为INF。
- 还有就是一些非常沙雕的越界了。
题目思路我就花了十几分钟……感谢刘晟大佬的帮助。
J 斐波那契和
给定n,k,求
比赛的时候找规律失败了
看不懂的官方题解,就算看懂了下次碰到还是不会
#include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int mod=998244353; int k; ll c[101][101]; struct ma { int m[203][203]; int n; ma(int k) { n=2*k+3; for(int i=0;i<n;i++) for(int j=0;j<n;j++) m[i][j]=0; } ma operator*(const ma &a) { ma ans(k); for(int i=0;i<n;i++) for(int k=0;k<n;k++) if(m[i][k]!=0) for(int j=0;j<n;j++) ans.m[i][j]=(ans.m[i][j]+1ll*m[i][k]*a.m[k][j]%mod)%mod; return ans; } }; void init() { for(int i=0;i<101;i++) c[i][0]=1; for(int i=1;i<101;i++) for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; } ma qpow(ma a,ll n) { ma ans(k); for(int i=0;i<ans.n;i++) ans.m[i][i]=1; while(n) { if(n&1) ans=ans*a; n>>=1; a=a*a; } return ans; } int main() { init(); ll n; int nn; cin>>n>>k; nn=2*k+3; if(n==1) return puts("1"),0; ma ans(k),mid(k); ans.m[0][0]=mid.m[0][0]=1; for(int i=1;i<nn;i++) { if(i&1) ans.m[0][i]=1; mid.m[i][0]=c[k][(i-1)/2]; } for(int j=1;j<nn;j++) for(int i=j;i<nn;i++) { if(j&1) mid.m[i][j]=c[k-(j-1)/2][(nn-i-1)/2]; else if(i%2==0) mid.m[i-1][j]=c[k-(j-1)/2][(nn-i-1)/2]; } ans=ans*qpow(mid,n-1); cout<<ans.m[0][0]<<endl; return 0; }
大概这就是出题人意图
然而这题可以用杜教筛板子
#include<bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define pb push_back typedef long long ll; #define SZ(x) ((ll)(x).size()) typedef vector<ll> VI; typedef pair<ll, ll> PII; const ll mod = 998244353; ll powmod(ll a, ll b) { ll res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1)res = res * a % mod; a = a * a % mod; }return res; } // head ll _, n; namespace linear_seq { const ll N = 10010; ll res[N], base[N], _c[N], _md[N]; vector<ll> Md; void mul(ll* a, ll* b, ll k) { rep(i, 0, k + k) _c[i] = 0; rep(i, 0, k) if (a[i]) rep(j, 0, k) _c[i + j] = (_c[i + j] + a[i] * b[j]) % mod; for (ll i = k + k - 1; i >= k; i--) if (_c[i]) rep(j, 0, SZ(Md)) _c[i - k + Md[j]] = (_c[i - k + Md[j]] - _c[i] * _md[Md[j]]) % mod; rep(i, 0, k) a[i] = _c[i]; } ll solve(ll n, VI a, VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+... // printf("%d\n",SZ(b)); ll ans = 0, pnt = 0; ll k = SZ(a); assert(SZ(a) == SZ(b)); rep(i, 0, k) _md[k - 1 - i] = -a[i]; _md[k] = 1; Md.clear(); rep(i, 0, k) if (_md[i] != 0) Md.push_back(i); rep(i, 0, k) res[i] = base[i] = 0; res[0] = 1; while ((1ll << pnt) <= n) pnt++; for (ll p = pnt; p >= 0; p--) { mul(res, res, k); if ((n >> p) & 1) { for (ll i = k - 1; i >= 0; i--) res[i + 1] = res[i]; res[0] = 0; rep(j, 0, SZ(Md)) res[Md[j]] = (res[Md[j]] - res[k] * _md[Md[j]]) % mod; } } rep(i, 0, k) ans = (ans + res[i] * b[i]) % mod; if (ans < 0) ans += mod; return ans; } VI BM(VI s) { VI C(1, 1), B(1, 1); ll L = 0, m = 1, b = 1; rep(n, 0, SZ(s)) { ll d = 0; rep(i, 0, L + 1) d = (d + (ll)C[i] * s[n - i]) % mod; if (d == 0) ++m; else if (2 * L <= n) { VI T = C; ll c = mod - d * powmod(b, mod - 2) % mod; while (SZ(C) < SZ(B) + m) C.pb(0); rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % mod; L = n + 1 - L; B = T; b = d; m = 1; } else { ll c = mod - d * powmod(b, mod - 2) % mod; while (SZ(C) < SZ(B) + m) C.pb(0); rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % mod; ++m; } } return C; } ll gao(VI a, ll n) { VI c = BM(a); c.erase(c.begin()); rep(i, 0, SZ(c)) c[i] = (mod - c[i]) % mod; return solve(n, c, VI(a.begin(), a.begin() + SZ(c))); } }; inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } ll f[10005]; int main() { ll n = read(), k = read(); f[1] = 1, f[2] = 1; for (int i = 3; i <= 10000; ++i) f[i] = (f[i - 1] + f[i - 2]) % mod; VI a; ll x = 0; for (int i = 1; i <= 10000; ++i) { x = (x + powmod(i, k) * f[i] % mod) % mod; a.push_back(x); } printf("%lld\n", linear_seq::gao(a, n - 1)); }
#include<bits/stdc++.h> using namespace std; #define lowbit(x) x&(-x) typedef long long ll; const int N = 2e5+5; ll tree[N],a[N]; int n, m; void add(int x, ll num) { while (x <= n) { tree[x] += num; x += lowbit(x); } } ll query(int x) { ll ans = 0; while (x) { ans += tree[x]; x -= lowbit(x); } return ans; } int main(){ cin>>n>>m; while (m--) { int x, y; cin>>x>>y; add(x, 1);//差分处理 add(y + 1, -1); } for(int i=1;i<=n;i++)a[i]=query(i); sort(a+1,a+1+n); ll ans=0; for(int i=1;i<=n;i++)ans+=a[i]*i; cout<<ans; return 0; }