
日记
我们拥有分数。
T1
题意:输入 和 ,求 。
我们首先用线性筛筛出 ,然后我们通过打表发现一个规律:
于是我们把后面的 展开:
然后我们就得到了一个 的算法,应对 还可以,但是那该死的出题人设的数据范围是 ,卡常也卡不过去,所以我们需要一些奇技淫巧把他弄成 的。
code:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
#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:
123456789101112131415161718192021222324252627
#include<bits/extc++.h>
#define int long long
using 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:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
#include<bits/extc++.h>
#define int long long
using 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;
}
然而苦于码力不足,自己打出来的死活调不出来,只好借鉴的汪汪的代码。