训练传送门:CUGBACM19级暑假训练#2(基础数学专题) - Virtual Judge (vjudge.net)
感觉其实还是板子题偏多的,但是还是我太菜了就做了一半不到qwq,想着都是各种板子索性整理一发在这,万一以后忘了啥的。
A - 组合数奇偶性(HDU 6129)
原题链接:acm.hdu.edu.cn
显然由于n是2e5,m是1e9的数据范围,直接模拟/暴力运算一定都是不行的
注意到,a^b=c,(a^b)^b=a;考虑到每一次操作之后,位置索引偏前的部分对后面的元素数值有着较大的影响,那么我们可以考虑每一个元素对后面元素异或的影响次数的奇偶性来判断这个位置的元素是否对后面元素的异或结果产生贡献,从而计算每一个元素的数值。
而:考虑到元素a1对后续元素的贡献次数:
a2 a3 a4 a5 a6……
当m=0时:0 0 0 0 0
当m=1时:1 1 1 1 1(由于后续每一个数的异或更新都必然包含a1)
当m=2时:1 2 3 4 5
当m=3时:1 3 6 10 15
……
可以观察到,第k+1次操作之后,p[k+1][wz]=sigma(p[k][i]),i=1,2,3……wz
那么这个就是简单的累次相加的板子,其中第i行第j列的元素数值为:C(j-1,m+i-1),规定m=0时值为0
找出组合数的通式之后,我们只需要判断它的奇偶性即可。对于组合数C(m,n),如果m&n==m时,组合数的数值为奇数,反之则为偶数。
代码(G++):
#include<iostream>
#define ll long long
using namespace std;
ll s[200005],p[200005];
bool zhsjo(ll m,ll n){
return (n&m)==m;
}
int main(){
ll t,n,m,i,j,k;
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++) scanf("%lld",&s[i]),p[i]=s[i];
for(i=1;i<=n-1;i++){
if(zhsjo(i,m+i-1)==1){
for(j=1;j<=n-i;j++) p[j+i]^=s[j];
}
}
for(i=1;i<=n;i++){
cout<<p[i];
if(i<n) cout<<" ";
}
cout<<endl;
}
return 0;
}
B - 笛卡尔树+树同构期望(待补)
C - 删子树期望(CF 280C)
原题链接:Problem - 280C - Codeforces
(其实是请教了曹佬才会做的qwq)
数学归纳法,考虑链状结构;平凡情形:n==1时期望必然是1
当n=k时,设当前的期望步数为q,那么当n=k+1时,有如下两种情况:
1. 有k/(k+1)的概率,取到前k个节点,由归纳知此时的期望为q*k/(k+1)
2.由1/(k+1)的概率,取到新加入的节点,那么取到之后问题子问题就变为了n=k的情形(期望步数+1),可得此时的期望为(q+1)/(k+1)
将1,2两式相加,可得和为:q+1/(k+1),便一般性的证明了新加入的节点期望贡献为1/(k+1)
当树状结构时,为了使新加入的每一个节点对原先的n个节点期望不产生影响,可知此时的节点必为叶子节点。由链状结构的归纳可知:此时的期望贡献为1/(d+1),其中d为该点的深度(即层数)
那么找到关系之后,简单的层序遍历bfs就可以了。
代码(GNU C++14):
#include<iostream>
#include<iomanip>
#include<vector>
#include<queue>
#define ll long long
using namespace std;
vector<ll>s[100005];
queue<pair<ll,ll> >cc;
pair<ll,ll>zczc;
ll p[100005];
long double su=0;
int main(){
ll n,i,j,k,zc,a,b;
char fh='\0';
scanf("%lld",&n);
getchar();
for(i=1;i<=n-1;i++){
scanf("%lld%lld",&a,&b);
s[a].push_back(b);
s[b].push_back(a);
}
cc.push({1LL,1LL});
while(cc.size()>0){
zczc=cc.front();
cc.pop();
if(p[zczc.first]==1) continue;
p[zczc.first]=1;
//cout<<zczc.second<<"---"<<endl;
su+=1.0/(zczc.second);
if(s[zczc.first].size()>0){
for(i=0;i<=s[zczc.first].size()-1;i++){
if(p[s[zczc.first][i]]==0){
cc.push({s[zczc.first][i],zczc.second+1});
}
}
}
}
cout<<fixed<<setprecision(9)<<su;
return 0;
}
D - Lindström–Gessel–Viennot定理(待补)
E - 欧拉降幂公式(待补)
F - johnson 法则(HDU 6299)
原题链接:acm.hdu.edu.cn
简单贪心题(和专题名称好像没啥关系awa),对于每一个括号串序列,首先按照以下方式链接:
1.全为左括号的序列优于所有其他序列。
2.全为右括号的序列次于所有其他序列。
概而言之,优先排列左括号即可。
然而,对于序列)))(((和序列((()))来说,后者必然位于前者之前,否则会出现左括号未匹配的情况。
那么,对于每一个序列,我们预先计算它的以下三个参数:
-1:孤立左括号个数(指在该序列中未与右括号配对的个数)
-2:孤立右括号个数
-3:已配对括号组个数(为方便后续处理加和,从而无需再重扫一边括号串)
那么,对于其他括号串:
3.只有孤立左括号的序列优于其他序列。
4.只有孤立右括号的序列次于其他序列。
5.孤立左括号个数多的,优先排列;孤立右括号少的,优先排列。
排序完成后,合法序列的总长度为:
(每一个合法序列内含的合法括号组个数+孤立左括号与孤立右括号的配对数)*2
代码(G++):
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
struct node{
ll z,y,sz,sl;
}s[100001];
char sr[100001];
void cl(ll cd,ll &a,ll &b,ll &sl){
ll i,j,zc=0;
for(i=0;i<=cd-1;i++){
if(zc==0 and sr[i]==')') b++;
if(zc!=0 and sr[i]==')') zc--,sl+=2;
if(sr[i]=='(') zc++;
}
a+=zc;
}
bool cmp(node x,node y){
if(x.y>=x.z and y.y<y.z) return 0;
if(x.y<x.z and y.y>=y.z) return 1;
if(x.y<x.z and y.y<y.z) return x.y<y.y;
else return x.z>y.z;
}
int main(){
ll t,n,i,j,k,a,b,zc,su;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(i=1;i<=n;i++){
scanf("%s",sr);
a=b=zc=0;
cl(strlen(sr),a,b,zc);
s[i].z=a;
s[i].y=b;
s[i].sz=a-b;
s[i].sl=zc;
}
sort(s+1,s+1+n,cmp);
for(i=1,a=su=0;i<=n;i++){
su+=s[i].sl;
if(i==1 or i==n){
if(i==1) a+=s[i].z;
else{
su+=min(a,s[i].y)*2;
break;
}
}
else{
zc=min(a,s[i].y);
su+=zc*2;
a-=zc;
a+=s[i].z;
}
}
printf("%lld\n",su);
}
return 0;
}
G - 绝对值不等式(HDU 6343)
原题链接:acm.hdu.edu.cn
大水题,久违了qwq
首先有绝对值不等式|a-b|+|b-c|>=|a-c|,这个可以由数轴距离的几何意义证明。
而:对于sqrt(|a-b|)+sqrt(|b-c|)与sqrt(|a-c|)
两边平方,有:|a-b|+|b-c|+2*sqrt(a-b)*sqrt(b-c)与|a-c|,由根号为非负数,前者较大。
那么可知:sqrt(|a-b|)+sqrt(|b-c|)>=sqrt(|a-c|)
答案即为第一个点至最后一个点的距离开方向下取整。
代码(G++):
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
#define dd long double
using namespace std;
int main(){
ll t,n,i,j,a,b,zc;
cin>>t;
while(t--){
cin>>n;
for(i=1;i<=n;i++){
cin>>zc;
if(i==1) a=zc;
if(i==n) b=zc;
}
cout<<int(sqrt(abs(b-a)))<<endl;
}
return 0;
}
H - 阿贝尔定理(待补)
I - simpson积分(HDU 1724)
原题链接:acm.hdu.edu.cn
不知道simpson积分是啥,直接求原函数赋值做的qwq
代码(G++):
#include<iostream>
#include<cmath>
#define ll long long
#define dd double
using namespace std;
dd a,b,l,r;
dd F(dd x){
dd zc=acos(x);
return zc/2-sin(2*zc)/4;
}
int main(){
ll t,sl;
dd i,j,k,su,dx,n;
scanf("%lld",&t);
while(t--){
scanf("%lf%lf%lf%lf",&a,&b,&l,&r);
su=2*a*b*(F(l/a)-F(r/a));
printf("%.3lf\n",su);
}
return 0;
}
J - 广义斐波那契循环节(待补)
K - 欧拉示性数(HDU 1418)
原题链接:acm.hdu.edu.cn
应该是全场最水的签到题,简单推式子就好了,答案是n+m-2
代码(G++):
#include<iostream>
#define ll long long
using namespace std;
int main(){
ll m,n;
while(cin>>n>>m){
if(n==0 and m==0) break;
cout<<n-2+m<<endl;
}
return 0;
}
L - LUCAS(待补)
M - Prüfer code(待补)、
N - 威尔逊定理(HDU 5391)
原题链接:acm.hdu.edu.cn
结论相当简单,实现感觉稍有难度qwq
当n为素数时,(n-1)!%n=n-1,当n不为素数时,(n-1)!%n=0
不过由于t是1e5,n是1e9的,根号n朴素做法会TLE,欧拉筛会MLE,所以就去洛谷bp了一个Miller Rabin qwq,然后就过了qwq
代码(G++):
#include<iostream>
#include<cmath>
#define ll long long
#define lll __int128
using namespace std;
//Ps: Miller Rabin copied from luo_gu(暂时不会qwq)
ll max_factor;
template<class type_name> inline type_name qr(type_name sample)
{
type_name ret=0,sgn=1;
char cur=getchar();
while(!isdigit(cur))
sgn=(cur=='-'?-1:1),cur=getchar();
while(isdigit(cur))
ret=(ret<<1)+(ret<<3)+cur-'0',cur=getchar();
return sgn==-1?-ret:ret;
}
inline ll gcd(ll a,ll b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
inline ll qp(ll x,ll p,ll mod)
{
ll ans=1;
while(p)
{
if(p&1)
ans=(lll)ans*x%mod;
x=(lll)x*x%mod;
p>>=1;
}
return ans;
}
inline bool mr(ll x,ll b)
{
ll k=x-1;
while(k)
{
ll cur=qp(b,k,x);
if(cur!=1 && cur!=x-1)
return false;
if((k&1)==1 || cur==x-1)
return true;
k>>=1;
}
return true;
}
inline bool prime(ll x)
{
if(x==46856248255981ll || x<2)
return false;
if(x==2 || x==3 || x==7 || x==61 || x==24251)
return true;
return mr(x,2)&&mr(x,61);
}
inline ll f(ll x,ll c,ll n)
{
return ((lll)x*x+c)%n;
}
inline ll PR(ll x)
{
ll s=0,t=0,c=1ll*rand()%(x-1)+1;
int stp=0,goal=1;
ll val=1;
for(goal=1;;goal<<=1,s=t,val=1)
{
for(stp=1;stp<=goal;++stp)
{
t=f(t,c,x);
val=(lll)val*abs(t-s)%x;
if((stp%127)==0)
{
ll d=gcd(val,x);
if(d>1)
return d;
}
}
ll d=gcd(val,x);
if(d>1)
return d;
}
}
int main(){
ll t,n,i,j,k,su;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
if(n<=10){
for(i=su=1;i<=n-1;i++){
su*=i;
su%=n;
}
printf("%lld\n",su);
}
else{
if(prime(n)) printf("%lld\n",n-1);
else printf("0\n");
}
}
return 0;
}
O - 毕达哥拉斯三元组(待补)
P - 勒让德定理(HDU 3988)
原题链接:acm.hdu.edu.cn
在正数n!的素因子标准分解式中,若素数p的最高指数记作Lp(n!)
则Lp(n!)=sigma(n/(p^k)),k>=1 ——摘自百度百科
勒让德定理的板子题。由于k不一定是素数,对k进行质因数分解,对于每一个质因数计算其次数后执行勒让德定理,维护所有质因子的最小值。
需要用欧拉筛维护1e8以内的质因数。
由于k的规模是1e14,n的规模是1e18,在判断乘方大小时最好采用除法来判断,否则容易爆long long。
代码(G++):
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
bool s[10000001];
ll p[1000001];
int main(){
ll t,k,dq,n,m,i,iii,j,zc,sl=0,slsl,dqdq,zx;
for(i=2;i<=10000000;i++){
if(s[i]==0) p[++sl]=i;
for(j=1;j<=sl and p[j]*i<=10000000;j++){
s[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
scanf("%lld",&t);
for(iii=1;iii<=t;iii++){
scanf("%lld%lld",&n,&k);
printf("Case %lld: ",iii);
if(k==1){
printf("inf\n");
continue;
}
for(i=2,zx=1e18;i<=sqrt(k);i++){
if(k%i==0){
slsl=0;
while(k%i==0) slsl++,k/=i;
dq=1,dqdq=0;
while(dq<=n*1.0/i){
dq*=i;
dqdq+=n/dq;
}
zx=min(zx,dqdq/slsl);
}
}
if(k!=1){
i=k;
slsl=1;
dq=1,dqdq=0;
while(dq<=n*1.0/i){
dq*=i;
dqdq+=n/dq;
}
zx=min(zx,dqdq/slsl);
}
printf("%lld\n",zx);
}
return 0;
}
Q - 法里数列(待补)
R - bell数(HDU 2512)
原题链接:acm.hdu.edu.cn
第二类斯特林数的板子题。
根据其递推式,可以得到状态转移方程:dp[i][j]=dp[i-1][j-1]+j*dp[i-1][j]
然后由于元素无序,得到的答案直接输出即可。
代码(G++):
#include<iostream>
#define ll long long
using namespace std;
ll s[2005][2005],p[2005],mod=1000;
int main(){
ll t,n,i,j,k,su;
s[1][1]=1;
for(i=2;i<=2000;i++){
for(j=1;j<=i;j++){
s[i][j]=s[i-1][j-1]+j*s[i-1][j];
s[i][j]%=mod;
}
}
for(i=1;i<=2000;i++){
for(j=su=0;j<=i;j++){
su+=s[i][j]%mod;
su%=mod;
}
p[i]=su;
}
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
printf("%lld\n",p[n]);
}
return 0;
}
S - stirling数(HDU 2643)
原题链接:acm.hdu.edu.cn
依然是第二类斯特林数的板子题。
较上题需要注意的是,由于元素有序,需要乘上其阶乘。
即:答案为sigma(dp[i][j]*(j!)),i<=n,j<=m
代码(G++):
#include<iostream>
#define ll long long
using namespace std;
ll s[105][105],p[105],mod=20090126;
ll jc(ll x){
ll i,s=1;
for(i=1;i<=x;i++){
s*=i;
s%=mod;
}
return s;
}
int main(){
ll t,n,i,j,k,su;
s[1][1]=1;
for(i=2;i<=100;i++){
for(j=1;j<=i;j++){
s[i][j]=s[i-1][j-1]+j*s[i-1][j];
s[i][j]%=mod;
}
}
for(i=1;i<=100;i++){
for(j=su=0;j<=i;j++){
su+=s[i][j]*jc(j)%mod;
su%=mod;
}
p[i]=su;
}
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
printf("%lld\n",p[n]);
}
return 0;
}
T - polya定理(待补)
U - pick定理(POJ 2954)
原题链接:2954 -- Triangle (poj.org)
格点三角形问题。格点三角形面积=(格点三角形内部定点数+边缘顶点数/2+1)
那么要求内部格点数,只要求出面积以及边缘顶点数即可。面积用向量的公式不会失去精度,边缘定点数只需要求一遍gcd即可。
代码(G++):
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
ll jdz(ll n){
if(n<0) return -n;
else return n;
}
ll gcd(ll a,ll b){
if(a==0 or b==0){
if(a==0) return b;
else return a;
}
return __gcd(a,b);
}
int main(){
ll x1,x2,x3,y1,y2,y3,i,j,k,a,b,c,d,s2,aa,bb,cc;
while(cin>>x1>>y1>>x2>>y2>>x3>>y3){
if(x1==0 and y1==0 and x2==0 and y2==0 and x3==0 and y3==0) break;
a=x2-x1;
b=y2-y1;
c=x3-x1;
d=y3-y1;
s2=jdz(a*d-b*c);
bb=0;
bb+=gcd(jdz(x1-x2),jdz(y1-y2))-1;
bb+=gcd(jdz(x1-x3),jdz(y1-y3))-1;
bb+=gcd(jdz(x3-x2),jdz(y3-y2))-1;
bb+=3;
aa=ceil((s2-bb+1)/2.0);
cout<<aa<<endl;
}
return 0;
}
V - 快速沃尔什变换(待补)
W - bsgs(待补)
X - pell方程(我也很想知道原题在哪qwq)
首先由平方和代换,x^2=(n+1)*(2*n+1)/6
注意到(n+1,2*n+1)=1,那么将x^2分拆为a^2和b^2,其中(a^2,b^2)=1
1. 6*a^2=n+1,b^2=2*n+1,此时有12*a^2-b^2=1
2. 3*a^2=n+1,2*b^2=2*n+1,此时有6*a^2-2*b^2=1,左偶右奇,舍去。
3. 2*a^2=n+1,3*b^2=2*n+1,此时有4*a^2-3*b^2=1
此时4*a^2=1+3*b^2,由模4意义可知:b必为奇数(两边摸4需为0)
那么:由3*b^2=2*n+1可知:n必为偶数。
又由于式子2*a^2=n+1,左偶右奇,矛盾,舍去。
4. a^2=n+1,6*b^2=2*n+1,此时有2*a^2-6*b^2=1,左偶右奇,舍去。
这样之后只剩下一个不定方程12*a^2-b^2=1,由于12系数不小,这个方程显然没有几组解,接下来暴力计算即可。
最后解出1e16内的几组解(n,k),打表即可。
代码(G++):
#include<iostream>
#define ll long long
using namespace std;
int main(){
ll n;
while(cin>>n){
if(n==0) break;
if(n>=18002650033695937) printf("18002650033695937 10393834843080975\n");
else{
if(n>=92799630902161) printf("92799630902161 53577891882061\n");
else{
if(n>=478361323441) printf("478361323441 276182038859\n");
else{
if(n>=2465845537) printf("2465845537 1423656585\n");
else{
if(n>=12710881) printf("12710881 7338631\n");
else{
if(n>=65521) printf("65521 37829\n");
else{
if(n>=337) printf("337 195\n");
else printf("1 1\n");
}
}
}
}
}
}
}
return 0;
}
Y - 线性基(待补)