CSP-S 考前数学练习
[HAOI2011] 向量
首先将题目转化,转化为求方程:
\(k(a,b)+q(b,a)+w(a,−b)+c(b,−a)=(x,y)\)
将这个方程再次化简,即为:
\((k+w)a+(q+c)b=x\)
\((k-w)b+(q-c)a=y\)
到这里,我们可以联想到 \(Bézout\) 定理,\(Bézout\) 定理为 \(ax+by=c\) , \(x\) 和 \(y\) 有整数解的充要条件是 \(gcd(a,b)∣c\)
所以,为了使 \(k+w,q+c,k-w,q-c\) 都为整数的的充要条件是 \(a\) 和 \(b\) 的最大公因数都可以整除 \(x, y\),即为 \(gcd(a,b)∣x\) 且 \(gcd(a,b)∣y\)
考虑分类讨论:
1.当 \(k+w,q+c,k-w,q-c\) 都是偶数时:
在 \(gcd(a,b)∣x\) 且 \(gcd(a,b)∣y\) 的基础上可以再提取一波公因数 \(2\), 所以此时为 \(2gcd(a,b)∣x\) 且 \(2gcd(a,b)∣y\)
2.当 \(k+w,q+c,k-w,q-c\) 都是奇数时:
\((k+w)a+(q+c)b=x\) 两边同时加上 \(a+b\),可得 \((k+w+1)a+(q+c+1)b=x+a+b\)
此时 \(2gcd(a,b)∣x + a + b\) 且 \(2gcd(a,b)∣y +a + b\)
3.当 \(k+w,k-w\) 是偶数,\(q+c,q-c\) 都是奇数时:
\((k+w)a+(q+c)b=x\) 两边同时加上 \(b\),可得 \((k+w)a+(q+c+1)b=x+b\)
此时 \(2gcd(a,b)∣x + b\),同理, \(2gcd(a,b)∣y + a\)
4.当 \(k+w,k-w\) 是奇数,\(q+c,q-c\) 都是偶数时:
\((k+w)a+(q+c)b=x\) 两边同时加上 \(a\),可得 \((k+w+1)a+(q+c)b=x+a\)
此时 \(2gcd(a,b)∣x + a\),同理, \(2gcd(a,b)∣y + b\)
所以我们只需要 check 一下该组数据是否符合上面任一一种情况就可以了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define rint register int
#define endl '\n'
#define int long long
int k;
bool f(int a, int b)
{
return (!(a % k)) and (!(b % k));
}
signed main()
{
int T;
scanf("%lld", &T);
while (T--)
{
int a, b, x, y;
scanf("%lld%lld%lld%lld", &a, &b, &x, &y);
k = std::__gcd(a, b) * 2;
if (f(x, y) || f(x + a, y + b) || f(x + b, y + a) || f(x + a + b, y + a + b))
{
puts("Y");
}
else
{
puts("N");
}
}
return 0;
}
UVA12775 Gift Dilemma
求方程 \(Ax+By+Cz=P\space \space \space \space \space \space (x\ge0,y\ge0,z\ge0)\) 非负整数解的个数。
设\(d=\gcd(A,B,C)\)
\(d\times(\frac{A}{d}x+\frac{B}{d}y+\frac{C}{d}z)=P\)
显然,方程成立一定会有 \(d|P\)
则 \(\frac{A}{d}x+\frac{B}{d}y+\frac{C}{d}z=\frac{P}{d}\)
\(\frac{A}{d}x+\frac{B}{d}y=\frac{P}{d}-\frac{C}{d}z\)
设 \(c=\frac{P}{d}-\frac{C}{d}z\)
所以 \(\frac{A}{d}x+\frac{B}{d}y=c\)
可以扩展欧几里得定理求出 \(\frac{A}{d}x+\frac{B}{d}y=\gcd(\frac{A}{d},\frac{B}{d})\) 的一组整数特解 \(x_0,y_0\)
\(\frac{A}{d}x_0+\frac{B}{d}y_0=\gcd(\frac{A}{d},\frac{B}{d})\)
将两个式子同时除以 \(\gcd(\frac{A}{d},\frac{B}{d})\) 再乘 \(c\) ,可以得到:
\(\frac{A}{d}\times\frac{x_0c}{\gcd(\frac{A}{d},\frac{B}{d})}+\frac{B}{d}\times\frac{y_0c}{\gcd(\frac{A}{d},\frac{B}{d})}=c\)
得到原方程的整数特解
\(x_1=\frac{x_0c}{\gcd(\frac{A}{d},\frac{B}{d})},y_1=\frac{y_0c}{\gcd(\frac{A}{d},\frac{B}{d})}\)
因为 \(\frac{A}{d}\times x_1+\frac{B}{d}\times y_1=c\)
所以 \(\frac{A}{d}\times (x_1- \frac{B}{d\times\gcd(\frac{A}{d},\frac{B}{d})})+\frac{B}{d}\times (y_1+\frac{A}{d\times\gcd(\frac{A}{d},\frac{B}{d})})=c\)
这一步怎么来的呢?因为 \((- \frac{B}{d\times\gcd(\frac{A}{d},\frac{B}{d})} \times \frac{A}{d}) + (\frac{A}{d\times\gcd(\frac{A}{d},\frac{B}{d})} \times \frac{B}{d}) = 0\)
给定 \(x_1,y_1\) 变化的倍数,就是得到了通解的形式,其中 \(s\) 为任意整数。
\(\frac{A}{d}\times (x_1- s\times\frac{B}{d\times\gcd(\frac{A}{d},\frac{B}{d})})+\frac{B}{d}\times (y_1+s\times\frac{A}{d\times\gcd(\frac{A}{d},\frac{B}{d})})=c\)
因为要求的是非负整数解,所以
\(x_1- s\times\frac{B}{d\times\gcd(\frac{A}{d},\frac{B}{d})}\ge0\)
\(y_1+s\times\frac{A}{d\times\gcd(\frac{A}{d},\frac{B}{d})}\ge0\)
我们接着进行转换:
\(x_1\ge s\times\frac{B}{d\times\gcd(\frac{A}{d},\frac{B}{d})}\)
\(y_1\ge -s\times\frac{A}{d\times\gcd(\frac{A}{d},\frac{B}{d})}\)
即为
\(x_1\times\frac{d\times\gcd(\frac{A}{d},\frac{B}{d})}{B}\ge s\)
\(-y_1\times\frac{d\times\gcd(\frac{A}{d},\frac{B}{d})}{A}\le s\)
所以
\(-y_1\times\frac{d\times\gcd(\frac{A}{d},\frac{B}{d})}{A}\le s\le x_1\times\frac{d\times\gcd(\frac{A}{d},\frac{B}{d})}{B}\)
原方程的非负整数解个数即为:\(\lfloor x_1\times\frac{d\times\gcd(\frac{A}{d},\frac{B}{d})}{B}\rfloor-\lceil-y_1\times\frac{d\times\gcd(\frac{A}{d},\frac{B}{d})}{A}\rceil+1\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define rint register int
#define endl '\n'
#define int long long
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int z = x;
x = y;
y = z - y * (a / b);
return d;
}
signed main()
{
int T;
scanf("%lld", &T);
int idx = 0;
while (T--)
{
idx++;
int A, B, C, P;
scanf("%lld%lld%lld%lld", &A, &B, &C, &P);
int x, y;
int ans = 0;
int d = exgcd(exgcd(A, B, x, y), C, x, y);
if (P % d)
{
printf("Case %lld: 0\n", idx);
continue;
}
int w = exgcd(A / d, B / d, x, y);
int x0, y0;
int x1, y1;
x0 = x;
y0 = y;
rint z = 0;
while (1)
{
if (P - z * C < 0)
{
break;
}
int c = P / d - C / d * z;
if (c % w)
{
z++;
continue;
}
x1 = x0 * c / w;
y1 = y0 * c / w;
ans += floor(x1 * d * 1.0 * w * 1.0 / B) - ceil(-y1 * d * 1.0 * w * 1.0 / A) + 1;
z++;
}
printf("Case %lld: %lld\n", idx, ans);
}
}
[SDOI2010] 古代猪文
题面很长,化简一下就是求 \(G^{\sum_{d|n}{C^{d}_{n}}}mod\ 999911659\)
我们发现 \(999911659\) 是个质数,不难想到欧拉定理,可以得到 \(G^{\sum_{d|n}{C^{d}_{n}}}\equiv\ G^{\sum_{d|n}C^{d}_{n}\ mod\ 999911658}\ (mod\ 999911659)\)。对于底数 \(G\) 和最终取模数 $mod $ \(999911659\) 可以直接用费马小定理求出。
所以现在需要解决的问题就是,如何求出 \(\sum_{d|n}{C^{d}_{n}\ mod\ 999911658}\)
这里需要用到中国剩余定理。
先对 \(999911658\) 进行质因数分解 \(999911658=2\times3\times4697\times35617\)
将 \(\sum_{d|n}C^{d}_{n}\) 分别对 \(999911658\) 的四个质因数取模,构建同余方程组,求出在模通解 \(999911658\) 的通解 \(t\) ,那么 $ t=\sum_{d|n}C^{d}_{n}mod\ 999911658$
最终答案即为:\(G^{t}\ mod\) \(999911659\)
\(\begin{cases}
x\equiv \sum_{d|n}C^{d}_{n}(mod\ 2)\\
x\equiv \sum_{d|n}C^{d}_{n}(mod\ 3)\\
x\equiv \sum_{d|n}C^{d}_{n}(mod\ 4679)\\
x\equiv \sum_{d|n}C^{d}_{n}(mod\ 35617)\\
\end{cases}\)
然后接着可以用Lucas定理求解,就做完了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define rint register int
#define endl '\n'
#define int long long
const int Mod = 999911659;
const int mod = 999911658;
const int N = 4e4 + 5;
int n, g;
int d[N], tot;
int p[10], cnt;
int fac[N], inv[N];
int a[10];
int qpow(int a, int b, int p)
{
int res = 1;
for (; b; b >>= 1)
{
if (b & 1)
{
res = (res * a) % p;
}
a = (a * a) % p;
}
return res;
}
void init(int p)
{
fac[0] = inv[0] = 1;
for (rint i = 1; i < p; i++)
{
fac[i] = fac[i - 1] * i % p;
inv[i] = qpow(fac[i], p - 2, p);
}
}
int C(int n, int m, int p)
{
if (m > n)
{
return 0;
}
return (inv[m] * inv[n - m] % p) * fac[n] % p;
}
int lucas(int n, int m, int p)
{
if (!m)
{
return 1;
}
return C(n % p, m % p, p) * lucas(n / p, m / p, p) % p;
}
int CRT()
{
int ans = 0;
for (rint i = 1; i <= cnt; i++)
{
int M = mod / p[i];
int t = qpow(M, p[i] - 2, p[i]);
//关于这个为什么求出来的是方程的解到现在也不太懂,之前都是用的exgcd
ans = (ans + t * M * a[i] % mod) % mod;
}
return ans;
}
void calc(int x)
{
init(p[x]);
for (rint i = 1; i <= tot; i++)
{
a[x] = (a[x] + lucas(n, d[i], p[x])) % p[x];
}
}
signed main()
{
scanf("%lld%lld", &n, &g);
if (g % Mod == 0)
{
puts("0");
return 0;
}
int t = mod;
for (rint i = 2; i <= sqrt(mod); i++)
{
if (!(t % i))
{
p[++cnt] = i;
while (t % i == 0)
{
t /= i;
}
}
}
if (t != 1)
{
p[++cnt] = t;
}
for (rint i = 1; i <= sqrt(n); i++)
{
if (!(n % i))
{
d[++tot] = i;
if (i * i != n)
{
d[++tot] = n / i;
}
}
}
for (rint i = 1; i <= cnt; i++)
{
calc(i);
}
printf("%lld", qpow(g, CRT(), Mod));
return 0;
}
[HNOI2002] 跳蚤
设题目中的那个长度为 \(n+1\) 的序列为 \(\{a_1...a_n,m\}\),这个题就是在求使 \(\sum_{i=1}^{n}a_i\times x_i+m\times x_{n+1}=1\) 有解的 \(a_1,a_2...a_n\) 有多少种。
由 \(Bézout\) 定理,可得方程的有解情况就是 \(\gcd(a_1,a_2...a_n,m)=1\)
但是直接去找方程有解的情况很麻烦且很难,可以反向考虑,求出上面那个方程无解的情况。
无解时,\(\gcd(a_1,a_2...,a_n,m)\not=1\)
先把 \(m\) 分解质因数,\(a_1...a_n\) 都一定有一个共同的 \(n\),就能算出使它无解的方案数了。
但是求无解显然有重复的情况,这个时候可以容斥原理,当 \(a_i\) 都为 \(x\) 的倍数时,此时方案数就是 $(\frac{m}{x})^{n} $
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define rint register int
#define endl '\n'
#define int long long
const int N = 1e2 + 5;
int n, m, cnt;
int a[N];
int ans;
signed main()
{
scanf("%lld%lld", &n, &m);
ans = pow(m, n);
int k = m;
for (rint i = 2; i <= sqrt(m); i++)
{
if (m % i == 0)
{
a[++cnt] = i;
while (m % i == 0)
{
m /= i;
}
}
}
if (m > 1)
{
a[++cnt] = m;
}
for (rint i = 1; i < 1 << cnt; i++)
{
int val = 1, num = 0;
for (rint j = 1; j <= cnt; j++)
{
if (i & (1 << (j - 1)))
{
num++;
val *= a[j];
}
}
if (num % 2)
{
ans -= pow(k / val, n);
}
else
{
ans += pow(k / val, n);
}
}
printf("%lld\n", ans);
return 0;
}
「KDOI-02」 一个仇的复
不难发现,肯定是由一堆横着的和一些竖着 \(2 \times 1\) 的构成的,而竖着的将将原来的网格分成了若干段。
只有一行时,设长度为 \(n\),用了 \(m\) 块。答案就是 \(\dbinom {n-1} {m-1}\)。拓展到两个并列的一行(即两行),答案是 \(\dbinom {2n-2} {m-1}\)。
枚举有 \(i\) 个竖着的,其中有 \(j\) 段。
先考虑剩下的分成 \(n-i\) 段的方案。要求方案数,首先我们要知道能插的个数。这时需要分类讨论。
若竖着没有在开头和结尾的,那么能插的个数为 \(2(n-1-i-j)\),还要划分的段数是 \(m-2(j+1)-i\),那么方案就是 \(\dbinom {2(n-1-i-j)} {m-2(j+1)-i}\)
若竖着只有一个在开头结尾,那么能插的个数为 \(2(n-i-j)\),还要划分的段数是 \(m-2j-i\),那么方案就是 \(\dbinom {2(n-i-j)} {m-2j-i}\)
若竖着的都在开头和结尾,那么能插的个数为 \(2(n+1-i-j)\),还要划分的段数是 \(m-2(j-1)-i\),那么方案就是 \(\dbinom {2(n+1-i-j)} {m-2(j-1)-i}\)
接着考虑竖着的方案,发现这个直接用插板法很难求,那么考虑分步来求。先考虑划分为 \(j\) 段,这个直接插板求出,方案是 \(\dbinom {i-1} {j-1}\),再考虑把这 \(j\) 段再插到原来的网格中,可以插在剩下位置的前面,但是再第一个的前面就不行,所以方案是 \(\dbinom {n-i-1} {j/j-1/j-2}\),下面取决于上面的分裂讨论,那么总的方案就是乘积。
考虑枚举有 \(i\) 个竖着的,其中有 \(j\) 段的方案就是三种情况的和乘上竖着的方案。
由于此题数据较大,卡不过去,所以只写了个 70pts 的代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#define rint register int
#define endl '\n'
#define int long long
using namespace std;
const int N = 4e7 + 5;
const int M = 5e3 + 5;
const int mod = 998244353;
int n, m, inv[N], fac[N];
int qpow(int a, int b)
{
int res = 1;
for (;b ;b >>= 1)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
}
return res;
}
void init()
{
fac[0] = inv[0] = 1;
for (rint i = 1; i <= 10000000; i++)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = qpow(fac[i], mod - 2);
}
}
int C(int n, int m)
{
return (inv[m] * inv[n - m] % mod) * fac[n] % mod;
}
signed main()
{
scanf("%lld%lld", &n, &m);
init();
int ans = C(2 * (n - 1), m - 2);
for (rint i = 1; i <= m; i++)
{
for (rint j = 1; j <= i; j++)
{
int tmp = j + 1;
if (tmp * 2 + i <= m)
{
int now1 = n - 1 - i - j;
int now2 = m - (tmp * 2 + i);
int res = C(i - 1, j - 1) * C(n - i - 1, j) % mod * C(2 * now1, now2) % mod;
ans = (ans + res) % mod;
}
tmp = j;
if (tmp * 2 + i <= m)
{
int now1 = n - i - j;
int now2 = m - (tmp * 2 + i);
int res = 2 * C(i - 1, j - 1) * C(n - i - 1, j - 1)%mod * C(2 * now1, now2)%mod;
ans = (ans + res) % mod;
}
tmp = j - 1;
if (tmp * 2 + i <= m && j >= 2)
{
int now1 = n + 1 - i - j;
int now2 = m - (tmp * 2 + i);
int res = C(i - 1, j - 1) * C(n - i - 1, j - 2) % mod * C(2 * now1, now2) % mod;
ans = (ans + res) % mod;
}
}
}
printf("%lld\n", ans + (n == m));
return 0;
}
CSP-S 考前数学练习的更多相关文章
- CSP2019心路历程
--人常说无论做什么都不要忘了初心,但如果一个人从来没有"应该"去忘了初心,又从何谈起的初心. CSP开考前,风吹在脸上,一些淡淡的回忆化作影子碎在地上:是到了一个令人感伤的路口了 ...
- CSP考前复习
前言 因为loceaner太菜了,他什么东西都不会 所以他打算学一个东西就记录一下 不过因为他很菜,所以他不会写原理-- 而且,他希望在2019CSP之前不会断更 就酱紫,就是写给他自己的--因为他太 ...
- noip2017考前基础复习——数论数学
·最大公约数 gcd 辗转相除法 gcd(a,b)=gcd(b,a%b) int gcd(int x,int y){ ) return x; return gcd(y,x%y); } 效率O(log ...
- NOIP&CSP 考前 Dev-cpp 的选项问题和考试心态
(进入考场后您将获得一个崭新的 \(Dev-cpp\),没有中文,没有编译选项,没有缺省源:我还将获得一个崭新的脑子,没有心态,没有智商,没有调试能力--) 中文 \[Step1 \] \[Step2 ...
- csp考前
T1不会太麻烦,不行心里多说几遍"沙比提,沙比提".就做出来了. 后天就要出发了,可是我感觉不到长进---- 可能又学一年是个不明智的想法,退役预定.
- 【CSP模拟赛】Confess(数学 玄学)
题目描述 小w隐藏的心绪已经难以再隐藏下去了.小w有n+ 1(保证n为偶数)个心绪,每个都包含了[1,2n]的一个大小为n的子集.现在他要找到隐藏的任意两个心绪,使得他们的交大于等于n/2. 输入描述 ...
- 【CSP模拟赛】坏天平(数学&思维)
蹭兄弟学校的题目做还不用自己出题的感觉是真的爽 题目描述 nodgd有一架快要坏掉的天平,这架天平右边的支架有问题,如果右边的总重量比左边多太多,天平就彻底坏掉了.现在nodgd手上有n种砝码,质量分 ...
- 【CSP模拟赛】方程(数学)
题目描述 求关于x的方程:x1+x2+……xk=n的非负整数解的个数. 输入格式 仅一行,包含两个正整数n,k. 输出格式 一个整数,表示方程不同解的个数,这个数可能很大,你只需输出mod 20080 ...
- CSP考前总结
10.2 考试: 1.数位DP 或者找规律 2.SB题,扫一遍找最大最小即可 3.莫比乌斯反演 出题人相出个数论和数据结构的综合题,但是找不到NOIP级别的,没办法只能忍痛割爱出个莫比乌斯,话说回来, ...
- 【csp模拟赛5】限制 (restrict.cpp)--数学
自己看吧: 爆搜代码: //春水初涨-春林初盛-春风十里-不如你 //----hzwer // 这是啥子题,读不懂-- //题意有问题 -- #include<iostream> #inc ...
随机推荐
- 信创啊,信创。Solon 的 war 包,现在同时支持 jakarta.servlet(及 javax.servlet)容器了!
Solon 是个神奇的项目,不是基于 Servlet 的.但是又很支持 Servlet,尤其是 war 包.打起来还挺方便的. 如果你是做信创的(听说,很多信创项目是用 war 部署到 tomcat ...
- 检测文件的格式——chardet模块
f3 = open(file=path,mode='rb') data = f3.read() # print(data) f3.close() result = chardet.detect(dat ...
- ValueError: Max value is 14 解决方案
方案一(有时会失效): 将EXCEL文件中的格式全部清除即可.最好是复制,然后只粘贴值. 方案二(指定引擎): data = pd.read_excel(path, engine="open ...
- Oracle 11g ocm考试内容目录
Server Configuration Create the database Determine and set sizing parameters for database structures ...
- 代码随想录算法训练营第四天|力扣24.两两交换链表节点、力扣19.删除链表的倒数第N个结点、力扣面试02.07链表相交、力扣142.环形链表
两两交换链表中的节点(力扣24.) dummyhead .next = head; cur = dummyhead; while(cur.next!=null&&cur.next.ne ...
- 使用C++界面框架ImGUI开发一个简单程序
目录 简介 使用示例 下载示例 main文件 设置ImGui风格 设置字体 主循环 添加Application类 中文编码问题 界面设计 关于imgui_demo.cpp 创建停靠空间 创建页面 隐藏 ...
- 安装iTerm2和oh-my-zsh
安装iTerm2和oh-my-zsh 此文是在参考许多教程(见目录:参考)并结合本人安装经历写下的一篇关于iTerm2和oh-my-zsh的认识和超级详细安装教程.全文所有图片均为本人截屏拍摄.希望能 ...
- Midjourney的一些学习心得:如何高效的图生图
注意本文没有什么长篇大论,全部是自己的学习心得. 心得体会:如何图生图 今天在一篇midjourney看到好图应该怎么抄. 相信经常会看到好图也想要的,但是要么抄不出感觉,要么抄过来把水印也抄了,这一 ...
- 使用HTML一键打包EXE工具打包KRPANO全景项目
HTML一键打包EXE工具(HTML封装EXE, HTML转EXE)能把任意HTML项目(网址)一键打包为单个EXE文件,可以脱离浏览器和服务器,直接双击即可运行. 打包工具群:429338543 最 ...
- 从零开始:Spring Security Oauth2 讲解及实战
OAuth2.0的四种授权模式: https://blog.csdn.net/weixin_30849403/article/details/101958273 1.授权服务配置: 配置一个授权服务, ...