比赛链接:The 2023 ICPC Xi’an Invitational Programming Contest - Dashboard - Contest - QOJ.ac
A
题意:给出一个 $n$ 的排列 $p$ ,$A$ 和 $B$ 轮流进行,每次可以将 $p_{1…p_1}$ 按照任意顺序排列,当某人对于同一个 $p_1$ 执行两次操作时(这个人执行了两次),这个人输掉比赛。$A$ 先手,求出 $n$ 的排列中 $B$ 赢得比赛的排列数。
首先,当某个人执行操作时,必须将序列打乱,否则另一个人保持原序,这个人再次操作时就会输掉比赛。
考虑 $B$ 必赢的情况:
对于初始排列 $p$ ,当在 $p_1,p_2,…,p_{p_1}$ 中 $p_1$ 是最小的时候 $B$ 必赢。因为此时不管 $A$ 怎么操作,$B$ 都能将 $p_1$ 重新排回第一个数。
而对于 $p_1$ 不是最小的数的时候,$A$ 先手操作时可以排列出另一个 $p_1$ 最小的情形使 $B$ 输掉比赛。
因此答案即为:
$$
\sum A_{n-i}^{i-1}A_{n-i}^{n-i}
$$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| # include <bits/stdc++.h>
using namespace std; const int N = 1e7 + 10, mod = 998244353; typedef long long LL; int n; LL fact[N], infact[N], inv[N];
int qp(int a, int k) { int res = 1; while(k) { if(k & 1) res = (LL)res * a % mod; a = (LL) a * a % mod; k >>= 1; } return res; }
void init() { fact[0] = infact[0] = 1; inv[1] = 1; for(int i = 2; i < N; i ++) inv[i] = (LL)(mod - mod / i) * inv[mod % i] % mod; for(int i = 1; i < N; i ++) { fact[i] = (LL)fact[i - 1] * i % mod; infact[i] = (LL)infact[i - 1] * inv[i] % mod; }
}
int A(int y, int x) { return (LL)fact[x] * infact[x - y] % mod; }
int main() { init(); cin >> n; LL res = 0; for(int i = 1; i <= (n + 1) / 2; i ++) { LL ans = (LL)A(i - 1, n - i) * fact[n - i] % mod; res = (res + ans) % mod; } cout << res << endl; return 0; }
|
E
给出一个网格,被分成很多个长方形,问能否通过将长方形按照长-长/宽-宽合并成一个整体。
没H难,,榜又歪了
考虑逆过程,能否将大长方形分割成多个长方形,因为肯定是两个长方形合并成一个大长方形,因此考虑分治做法。
首先预处理,以列为例,预处理出网格的每一列有没有完全贯穿,若完全贯穿则说明可能从这里分开;行同理。
在dfs时枚举以网格的某一列分割是否可行,若这一列可以分成两部分那么分治依次讨论两部分;首先讨论按列分割,然后讨论按行分割。
注意剪枝:若以某一列分割时左边能够完全分割,但是右边不能分割,那么直接retuen 0
结束。只有在左边不能完全分割时再往右枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include<bits/stdc++.h> using namespace std; const int N=1510; int n,m; char col[N][N],row[N][N]; int righ[N][N],down[N][N]; bool dfs(int x1,int y1,int x2,int y2){ bool tm1=0,tm2=0; bool ret=1; for(int i=x1+1;i<x2;++i){ if(righ[i][y1]!=y1) ret=0; if(righ[i][y1]<y2) continue; tm1=dfs(x1,y1,i,y2); tm2=dfs(i,y1,x2,y2); return (tm1&&tm2); } for(int i=y1+1;i<y2;++i){ if(down[x1][i]!=x1) ret=0; if(down[x1][i]<x2) continue; tm1=dfs(x1,y1,x2,i); tm2=dfs(x1,i,x2,y2); return (tm1&&tm2); } return ret; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<n;++i) scanf("%s",row[i]+1); for(int i=1;i<=n;++i) scanf("%s",col[i]+1); for(int i=1;i<n;++i){ righ[i][m]=m; for(int j=m;j>=1;--j){ if(row[i][j]=='1') righ[i][j-1]=righ[i][j]; else righ[i][j-1]=j-1; } } for(int j=1;j<m;++j){ down[n][j]=n; for(int i=n;i>=1;--i){ if(col[i][j]=='1') down[i-1][j]=down[i][j]; else down[i-1][j]=i-1; } } if(dfs(0,0,n,m)) puts("YES"); else puts("NO"); }
|
G
水题
1 2 3 4 5 6 7 8 9
| #include<bits/stdc++.h> using namespace std; int main() { int n; cin>>n; for(int i=1;i<=n;i++)cout<<i<<" "; return 0; }
|
H
题意:有一个分成 $n$ 块的大轮子,同时内圈有对应 $n$ 个小轮子,初始内圈每一块都是0,外圈按顺时针每块依次标有 $0,1,…,n-1$ 。有两种操作,一种是将外圈的数加到对应的内圈上,另一种是将内圈旋转到任一角度。先给出内圈最终序列,求能否达到最终状态并求最小次数。
每一次加的都是一组等差数列,因此考虑差分。
- 若最终序列能够达到,那么最终序列的循环差分的模必须相同。要么有正有负,要么全零。
- 考虑若差分的值全零,且数组 $a$ 不是全零,那么可以模拟得到最小操作数为 $n+1$ 。具体操作为:先加 $n-1$ 次,然后把0移动到目标处再加一次。
- 若其他情况,答案为模值+1。可以证明一定可以直接加模值次数然后通过交换得到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include<bits/stdc++.h> #define int long long using namespace std; int n,ans,rst; int a[100010],b[100010]; signed main(){ scanf("%lld",&n); for(int i=1;i<=n;++i) scanf("%lld",a+i); for(int i=1;i<=n;++i) b[i]=a[i%n+1]-a[i]; rst=ans=(b[1]+n)%n; for(int i=1;i<=n;++i){ if(((b[i]+n)%n!=rst)){ puts("-1"); return 0; } } if(a[1]!=0) ans++; if(rst==0&&a[1]!=0) ans=n+1; printf("%lld",ans); }
|
J
题意:给出两个0-1的实数 $x,y$ 。对 $x$ 进行操作得到 $y$ 。第一中操作是 $x=x\times 0.5$,第二种是 $x=(x-1)\times 0.5+1$ 。求一种操作方案。
观察到操作2相当于将 $x$ 除以2然后加上0.5。
直接先对 $x$ 操作1直至变为 $0$ ,然后从 $0$ 变为 $y$ 。
把 $y$ 先乘2的50次方变成整数。
从后往前遍历 $y$ 的每一位,若为1,进行操作2,否则进行操作1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <bits/stdc++.h>
using i64 = long long;
int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); double a, b; std::cin >> a >> b; i64 B = (1LL << 50) * b; for (int i = 0; i < 50; i++) { std::cout << (B >> i & 1) + 1; } std::cout << "\n"; return 0; }
|