我们拥有分数。
T1
题意:输入 和 ,求 。
我们首先用线性筛筛出 ,然后我们通过打表发现一个规律:
于是我们把后面的 展开:
然后我们就得到了一个 的算法,应对 还可以,但是那该死的出题人设的数据范围是 ,卡常也卡不过去,所以我们需要一些奇技淫巧把他弄成 的。
code:
#include<bits/extc++.h>#pragma GCC optimize(2)#pragma GCC optimize(3)#pragma GCC optimize("Ofast")#pragma GCC target("avx")#define re register#define int long long#define Inv(x) binpow(x,mod - 2)using namespace std;const int mod = 1e9 + 7;const int maxn = 1e7 + 5;int n,p;bool ipr[maxn];int pr[maxn],cnt;int phi[maxn],inv[maxn],pw[maxn],low[maxn];inline int binpow(int x,int y){ re int ret = 1; while (y) { if (y & 1) ret = ret * x % mod; x = x * x % mod; y >>= 1; } return ret;}inline void prime(int up){ phi[1] = 1; for (re int i = 2; i <= up; i++) { if (!ipr[i]) { pr[++cnt] = i; low[i] = i; phi[i] = i - 1; inv[i] = Inv(i); pw[i] = binpow(i,p); } for (re int j = 1; j <= cnt; j++) { if (i * pr[j] > up) break; ipr[i * pr[j]] = 1; low[i * pr[j]] = pr[j]; phi[i * pr[j]] = phi[i] * phi[pr[j]]; if (i % pr[j] == 0) { phi[i * pr[j]] = phi[i] * pr[j]; break; } } }}signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> p; prime(n); inv[1] = pw[1] = 1; for (int i = 2; i <= n; i++) { pw[i] = pw[low[i]] * pw[i / low[i]] % mod; inv[i] = inv[low[i]] * inv[i / low[i]] % mod; } re int ans = p; for (re int i = 2; i <= n; i++) ans = (ans + phi[i] * (pw[i] - 1 + mod) % mod * inv[i - 1] % mod) % mod; cout << ans; return 0;}一定把线性筛求欧拉函数给记住咯!
T2
题意:给定一颗有 个节点的二叉树,每个节点上有一个 的编号,满足任意除叶子节点的编号一定比两个子节点的编号大。钦定有 个节点一定是叶子节点,输入 和那 个叶子节点的编号,求总共可以有多少种二叉树形态。
我们从大到小,模拟把每个数填进去的过程。设 表示这个节点有多少个填法。那么最开始就肯定是 ,因为只有根节点这一个位置可以填。如果这个位置为钦定为叶子节点,那么 ,因为它会占一个位置,否则 ,因为即使它占了一个位置,它也会创造两个新的位置。
code:
#include<bits/extc++.h>#define int long longusing namespace std;const int maxn = 1e6 + 5;const int mod = 1e9 + 7;int n,m;bool b[maxn];signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m; for (int i = 1,x; i <= m; i++) { cin >> x; b[x] = 1; } int ans = 1,tmp = 1; for (int i = n; i >= 1; i--) { ans = ans * tmp % mod; tmp += b[i] ? -1 : 1; } cout << ans; return 0;}T3
没改,神秘小 dp。
T4
我们设这 个数的异或和是 。那么当 时很明显是平局。否则,他们一定靠 的最高位决出胜负。设 的最高位是 ,那么把 提出来,我们就得到了一个 串。且 的数量为奇数。
我们注意到,当 是偶数的时候,要么在 为偶数的地方有奇数个 ,要么在 为奇数的地方有。由于先手可以决定后手取的地方下标的奇偶性,所以当 为偶数的时候,先手必胜。
那么当 为奇数呢?为了不让后手变成上面的先手,所以先手一定要在第一次取一个 ,使 的个数变成偶数,不然,后手变先手然后就必胜了。取完第一个 过后,我们发现先手一定要和变成先手的后手取的一样。那么,我们把两边一样的都取完,那么剩下的肯定得是这个样子:,也就是对于一个 ,有 。不然就不能和变成先手的后手取一样的了。
然后还有一个:就是 的个数必须是 的倍数,因为先手和后手会把每个 都取完。如果不是 的倍数,那么先手的手上就会有奇数个 ,加上最前面的 就败了。
code:
#include<bits/extc++.h>#define int long longusing namespace std;const int maxn = 1e5 + 5;int n,sum;int a[maxn];bool calc(int s,int t){ while (a[s] == a[t] && s < t) s++,t--; if (s > t) return 1; for (int i = s; i <= t; i += 2) if (a[i] != a[i + 1]) return 0; return 1;}void solve(){ scanf("%lld",&n); sum = 0; for (int i = 1; i <= n; i++) { scanf("%lld",a + i); sum ^= a[i]; } if (sum == 0) return (void)puts("Draw"); int pos = 30; while (pos >= 0) { if (sum & (1 << pos)) break; pos--; } for (int i = 1; i <= n; i++) a[i] = (a[i] >> pos) & 1; sum = 0; for (int i = 1; i <= n; i++) sum += a[i]; if ((n & 1) == 0) return (void)puts("Alice"); if (!a[1] && !a[n]) puts("Bob"); else if (sum % 4 == 1) { if (a[1] && a[n]) { if (calc(1,n - 1) || calc(2,n)) puts("Alice"); else puts("Bob"); } else if (a[1]) { if (calc(2,n)) puts("Alice"); else puts("Bob"); } else { if (calc(1,n - 1)) puts("Alice"); else puts("Bob"); } } else puts("Bob");}signed main(){ int t; scanf("%lld",&t); while (t--) solve(); return 0;}然而苦于码力不足,自己打出来的死活调不出来,只好借鉴的汪汪的代码。
