2019年9月14日(数论专题考试)
Posted alanallen21love28
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019年9月14日(数论专题考试)相关的知识,希望对你有一定的参考价值。
汗~,差点爆零(QwQ)……
(prob2:saber)
(upd):题意以后都不写了,反正写了日期,去文件里找。
题目数学?可惜我开始跑的是暴力……
思路肯定是总方案减去不合法方案,那么就有两种主流思路:
暴力30分,因为不合法情况是且仅是要到达灰线上面一条线的任一个点,那么可以算方案。考虑(dp)式,设(f_i)为到达目标线上的第(i)个点且未经过之前(i-1)个点的总方案数,(s_i)表示目标线上第(i)个点到起点的方案数,(t_i)表示目标线上第(i)个点到终点的方案数,(p_{i,j}(i<j))表示目标线上第(i)个点到第(j)个点的方案数,则
[f_i=s_i-sum_{j=1}^{i-1}f_j* p_{j,i}]
[ans=C_{n+m}^m-sum_{i=1}^mf_i* t_i]
时间复杂度(O(m^2)),实际得分40分:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
int x=0;
char ch=getchar();
for(;!isalnum(ch);ch=getchar());
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
const int mod=2e7-1;
const int xx=1e5+1612;
int jc[xx],ny[xx],f[xx];
inline int power(int x,int k)
{
int res=1;
for(;k;x=x*x%mod,k>>=1) if(k&1) res=res*x%mod;
return res;
}
inline int C(int n,int m)
{
return jc[n]*ny[m]%mod*ny[n-m]%mod;
}
jinitaimei main()
{
int n=in,m=in;
jc[0]=ny[0]=1;
fur(i,1,n+m) jc[i]=jc[i-1]*i%mod,ny[i]=power(jc[i],mod-2);
fur(i,0,m-1) f[i+1]=C(i+i+2,i);
fur(i,1,m) fur(j,1,i-1) f[i]=(f[i]-f[j]*C((i-j)<<1,i-j)%mod+mod)%mod;
int ans=C(n+m,n);
fur(i,1,m) ans=(ans-f[i]*C(n-(i-1)+m-(i+1),n-(i-1))%mod+mod)%mod;
cout<<ans<<endl;
return 0;
}
正解,发现对称算一下(ans=C_{n+m}^m-C_{n+m}^{m-2})
虽然(n,m)比较大,但发现(mod=2e7-1)比较小,于是卢卡斯
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
int x=0;
char ch=getchar();
for(;!isalnum(ch);ch=getchar());
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
const int mod=2e7-1;
int jc[mod];
inline int power(int x,int k)
{
int res=1;
for(;k;x=x*x%mod,k>>=1) if(k&1) res=res*x%mod;
return res;
}
inline int lucas(int n,int m)
{
if(n<mod&&m<mod)
{
if(n<m) return 0;
return jc[n]*power(jc[m],mod-2)%mod*power(jc[n-m],mod-2)%mod;
}
return lucas(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
}
jinitaimei main()
{
int n=in,m=in;
jc[0]=1;
fur(i,1,mod) jc[i]=jc[i-1]*i%mod;
cout<<(lucas(n+m,m)-lucas(n+m,m-2)+mod)%mod<<endl;
return 0;
}
于是正解比暴力还短……
(prob3:)不够优秀((good))
很明显发现这个东西其实是求(sum_{i=1}^nd_i* (d_i-1)/2)
70分就是暴力求,时间复杂度(O(nsqrt n)):
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
int x=0;
char ch=getchar();
for(;!isalnum(ch);ch=getchar());
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
const int mod=998244353;
const int ny2=499122177;
inline int power(int x,int k)
{
int res=1;
for(;k;x=x*x%mod,k>>=1) if(k&1) res=res*x%mod;
return res;
}
jinitaimei main()
{
int n=in;
if(n==1)
{
cout<<"0"<<endl;
return 0;
}
int ans=0;
fur(i,2,n)
{
int all=0,j=i;
for(int k=1;k*k<=j;++k) if(!(j%k))
{
++all;
if(k*k!=j) ++all;
}
ans=((all-1)*all%mod*ny2%mod+ans)%mod;
}
printf("%lld
",ans);
return 0;
}
正解线性筛即可,时间复杂度(O(n)):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
int x=0;
char ch=getchar();
for(;!isalnum(ch);ch=getchar());
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
const int mod=998244353;
const int ny2=499122177;
int d[10000001],e[10000001];
int prime[10000001],n,ans=0,cnt=0;
bool to[10000001];
inline void init()
{
fur(i,2,n)
{
if(!to[i])
{
prime[++cnt]=i;
d[i]=2;
e[i]=1;
}
fur(j,1,cnt)
{
if(i*prime[j]>n) break;
to[i*prime[j]]=true;
if(i%prime[j])
{
d[i*prime[j]]=d[i]*2;
e[i*prime[j]]=1;
}
else
{
d[i*prime[j]]=d[i]/(e[i]+1)*(e[i]+2);
e[i*prime[j]]=e[i]+1;
break;
}
}
}
}
jinitaimei main()
{
n=in;
init();
fur(i,1,n) ans=(ans+d[i]*(d[i]-1)%mod*ny2%mod)%mod;
cout<<ans<<endl;
return 0;
}
(prob1:)摆干草((bales))
额,没想到是数据结构……
二分,然后并查集维护最小区间,查看与现在搜到的是否矛盾即可
每当查到一个(mid),将(mid)及之前的按(A)值从大到小排序,查看是否有大区间覆盖小区间即可
这个二分有点毒瘤……
时间复杂度(O(Nalpha NlogQ)):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
//#define int long long
inline int read()
{
int x=0;
char ch=getchar();
for(;!isalnum(ch);ch=getchar());
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
const int xx=1e6+1612,yy=2e5+1612;
int fa[xx],n,m;
struct line
{
int l,r;
int val;
}L[yy],tmp[yy];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline bool cmp(line x,line y){return x.val>y.val;}
inline bool check(int x)
{
fur(i,1,n+1) fa[i]=i;
fur(i,1,x) tmp[i]=L[i];
sort(tmp+1,tmp+1+x,cmp);
int l1,l2,r1,r2;
l1=l2=tmp[1].l;r1=r2=tmp[1].r;
fur(i,2,x)
{
if(tmp[i].val<tmp[i-1].val)
{
if(find(l1)>r1) return false;
for(int j=fa[l2];j<=r2;j=fa[j]) find(j),fa[j]=find(j+1);
l1=l2=tmp[i].l;
r1=r2=tmp[i].r;
}
else
{
l1=max(l1,tmp[i].l);
l2=min(l2,tmp[i].l);
r1=min(r1,tmp[i].r);
r2=max(r2,tmp[i].r);
if(r1<l1) return false;
}
}
if(find(l1)>r1) return false;
return true;
}
jinitaimei main()
{
freopen("bales.in","r",stdin);
freopen("bales.out","w",stdout);
n=in,m=in;
fur(i,1,m) L[i]=(line){in,in,in};
int l=1,r=m+1;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid+1;
else r=mid;
}
if(l>m) puts("0");
else printf("%d
",l);
return 0;
}
(prob4:)闷声发大财((rich))
30分:考虑状压,明确状态(f_{A,B})为龙选(A)而(hyj)选(b)的方案数,压的是因数
压根没想到,于是没写,不给代码了……
正解:考虑状压优化,将(2-n)的每个数(i)的(sqrt i)之内的质因数状压出来,其余暴力枚,用数组(f_{0/1,A,B})表示即为龙或(hyj)选了当前数(i)的大于(sqrt i)的那一个质因数且龙集合(A)、(hyj)集合(B)的方案数,可以推出来递推式……
在(sqrt {500})以内的质数只有(8)个,可以放心状压……
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
int x=0;
char ch=getchar();
for(;!isalnum(ch);ch=getchar());
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}
const int maxp(8),maxn(501);
int prime[8]={2,3,5,7,11,13,17,19};
int n,mod,ans=0;
int f[2][1<<maxp][1<<maxp],g[1<<maxp][1<<maxp];
struct number{int numset,bignum;}a[maxn];
inline bool cmp(number x,number y){return (x.bignum^y.bignum)?(x.bignum>y.bignum):(x.numset<y.numset);}
inline void up(int &x,int y){(x+=y)%=mod;}
inline void init()
{
n=in;mod=in;
fur(i,2,n)
{
int x=i;
fur(j,0,7) if(!(x%prime[j]))
{
a[i].numset+=(1<<j);
while(!(x%prime[j])) x/=prime[j];
}
a[i].bignum=x;
}
sort(a+2,a+n+1,cmp);
g[0][0]=1;
}
inline void sol()
{
fur(i,2,n)
{
if(i==2||a[i].bignum==1||a[i].bignum^a[i-1].bignum) memcpy(f[0],g,sizeof(g)),memcpy(f[1],g,sizeof(g));
fdr(j,(1<<maxp)-1,0) fdr(k,(1<<maxp)-1,0)
{
if(!(j&a[i].numset)) up(f[0][j][k|a[i].numset],f[0][j][k]);
if(!(k&a[i].numset)) up(f[1][j|a[i].numset][k],f[1][j][k]);
}
if(i==n||a[i].bignum==1||a[i].bignum^a[i+1].bignum)
fdr(j,(1<<maxp)-1,0) fdr(k,(1<<maxp)-1,0) g[j][k]=(f[0][j][k]+f[1][j][k]-g[j][k]+mod)%mod;
}
}
jinitaimei main()
{
init();
sol();
fdr(j,(1<<maxp)-1,0) fdr(k,(1<<maxp)-1,0) if(!(j&k)) up(ans,g[j][k]);
cout<<ans<<endl;
return 0;
}
(ps:xg)真是一只毒瘤的鸡
以上是关于2019年9月14日(数论专题考试)的主要内容,如果未能解决你的问题,请参考以下文章