临近JSCPC,特此把以前做过的数论题撸出来整理板子
C - Aladdin and the Flying Carpet
题意:告诉矩形的面积a,以及边长不得小于b,问有几种方案
不知道代码为啥wa了
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
const int N=1e6+100;
int prime[N];
int tag[N];
int cnt=0;
typedef long long LL;
void getp()
{
memset(tag,0,sizeof(tag));
tag[0]=1;
tag[1]=1;
for(int i=2;i<N;++i)
{
if(!tag[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&prime[j]*i<N;++j)
{
tag[prime[j]*i]=1;
if(i%prime[j]==0)
break;
}
}
// for(int i=1;i<=10;++i)
// cout<<prime[i]<<' ';
// cout<<endl;
}
long long calc(long long x)//计算x的因子个数 为(1+alpha1)*(1+alpha2)...即素因子的幂次
{
long long ans=1;
for(int i=1;i<=cnt&&prime[i]*prime[i]<=x;i++)
{
if(x%prime[i]==0)
{
int m=0;
while(x%prime[i]==0)
{
x/=prime[i];
m++;
}
//cout<<prime[i]<<' '<<m<<' ';
ans*=(1+m);
// cout<<ans<<endl;
}
}
if(x>1) ans*=2; //说明x是素数
ans/=2;
//printf("%lld\n",ans);
}
int main()
{
getp();
int T;
scanf("%d",&T);
for(int i=1;i<=T;++i)
{
long long a,b;
scanf("%lld%lld",&a,&b);
LL ans=0;
if(b*b>a) //边的下界的平方大于面积 无解
{
ans=0;
printf("Case %d: %lld\n",i,ans);
}
else
{ ans=calc(a);
for(LL j=1;j<b;++j) //小于b的答案无效
if(a%j==0)
ans--; //前面已经除过2了 且b*b<=a 不用担心多减
printf("Case %d: %lld\n",i,ans);
}
}
return 0;
}
题意:为n以内有多少数对的LCM为n
L - Fantasy of a Summation 快速指数幂
给出下面代码要求优化。
思路:res一共执行了n^k次,那么总共加了k*n^k的个数,可知每个数被加的次数是一样的,所以每个数都被加了k*n^(k-1)次。所以最终答案就是所有数的和*k*n^(k-1)
#include <stdio.h>
int cases, caseno;
int n, K, MOD;
int A[1001];
int main() {
scanf("%d", &cases);
while( cases-- ) {
scanf("%d %d %d", &n, &K, &MOD);
int i, i1, i2, i3, ... , iK;
for( i = 0; i < n; i++ ) scanf("%d", &A[i]);
int res = 0;
for( i1 = 0; i1 < n; i1++ ) {
for( i2 = 0; i2 < n; i2++ ) {
for( i3 = 0; i3 < n; i3++ ) {
...
for( iK = 0; iK < n; iK++ ) {
res = ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;
}
...
}
}
}
printf("Case %d: %d\n", ++caseno, res);
}
return 0;
}
//A
#include<iostream>
#include<stdio.h>
using namespace std;
typedef long long LL;
LL qmod(LL a,LL b,LL mod)
{
if(b==0) return 1;
LL ans=1;
// cout<<"a: "<<a<<endl;
while(b>0)
{
if(b&1) ans=ans*a%mod;
// cout<<"ans: "<<ans<<endl;
a=a*a%mod;
b=b>>1;
}
return ans;
}
int main()
{
int T;
cin>>T;
int kase=0;
while(T--)
{
++kase;
LL n,k,mod;
cin>>n>>k>>mod;
LL sum=0;
for(int i=1;i<=n;++i)
{
LL x;
cin>>x;
sum=(sum+x)%mod;
}
//cout<<sum<<endl;
LL ans=qmod(n,k-1,mod);
//cout<<ans<<endl;
ans=ans*k%mod*sum%mod;
printf("Case %d: %lld\n",kase,ans);
}
return 0;
}
记得很久以前按照别人的思路写的T了
中国剩余定理和扩展中国剩余定理讲解 比较清晰,但是代码不是很符合我的习惯
题意:有一个正整数N满足C个条件,每个条件都形如“它除以X的余数在集合{Y1,Y2...Yk}中”,所有条件中的X两两互素,你的任务是找出最小的S个解。按照从小到大排序。
这是我T了的代码。
//T
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<string.h>
#include<stdio.h>
using namespace std;
typedef long long LL;
const int N=15;
const int M=105;
int x[N],c,s;
int k[N];
int y[N][M];
LL a[N];
LL Mul=1;
int now;
set<int>value[N];
vector<LL> ans; //存一种组合得到的ans,通过它加M的整数倍获得其他答案,ans[i]是在i组搭配下的最小答案了,不应该用vector 应该用set的
LL extgcd(LL a,LL b,LL &X,LL &Y) //O(lgn)
{
if(!b) {X=1;Y=0;return a;}
LL d=extgcd(b,a%b,Y,X);
Y-=a/b*X;
return d;
}
LL CRT()
{
LL ans=0;
/*Mul=1;
for(int i=0;i<c;++i)
Mul*=x[i];*/
// cout<<Mul<<endl; 一开始bug在M不应该在这里计算,还是重复计算了
for(int i=0;i<c;++i) //事实上这里也不需要重复计算四次的,再开个位置存mi关于x[i]的逆就好
{
LL mi=Mul/x[i];
LL X,Y;
extgcd(mi,x[i],X,Y);
ans=(ans+a[i]*X*mi)%Mul;
}
return (ans+Mul)%Mul;
}
void dfs(int layer) //分配a[N]
{
if(layer==c)
{
/*for(int i=0;i<c;++i)
cout<<a[i]<<endl;*/ //a[i]的分配是正确的
ans.push_back(CRT()); //已经获得一组A
return ;
}
for(int i=0;i<k[layer];++i)
{
a[layer]=y[layer][i];
dfs(layer+1);
}
}
void solve_CRT()
{
ans.clear();
dfs(0); //生成所有的组合并获得所有的CRT值
sort(ans.begin(),ans.end());
// for(int j=0;j<ans.size();++j)
// cout<<ans[j]<<endl;
for(int i=0;;++i)
for(int j=0;j<ans.size();++j)
{
LL n=Mul*i+ans[j]; //ans[j]是小于M的所以这样排一遍一定是最小的
//cout<<n<<endl;
if(n>0) {--s; cout<<n<<endl; if(s==0) return ;}
}
}
void solve_enum()
{
for(int i=0;i<c;++i)
{
if(i==now) continue; //不在选择的那一个k/x下选答案
value[i].clear();
for(int j=0;j<k[i];++j)
value[i].insert(y[i][j]);
}
for(int t=0;;++t) //枚举x*t+y(t=1,2,3......)
{
for(int i=0;i<k[now];++i) //枚举y的值
{
LL n=x[now]*t+y[now][i];
if(n==0) continue; //去换下一个y
bool ok=true;
for(int j=0;j<c;++j)
{
if(j==now) continue;
if(!value[j].count(n%x[j])) {ok=false;break;} //去换下一个i
}
if(ok) //所有的x都能被n整除
{
--s;
cout<<n<<endl;
if(s==0)
return ;
}
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
while(cin>>c>>s&&c&&s)
{
now=0;
for(int i=0;i<c;++i)
{
cin>>x[i]>>k[i];
Mul*=x[i];
if(k[now]*x[i]>k[i]*x[now]) //选一个k/x尽量小的
now=i;
for(int j=0;j<k[i];++j)
cin>>y[i][j];
sort(y[i],y[i]+k[i]); //可能的余数从小到大排序
}
if(Mul>10000) solve_enum();
else solve_CRT();
cout<<endl;
}
return 0;
}
题意:给你M*N的格子,其中B个格子被封锁了。要将所有未被封锁的格子上色,每个格子可以上k种颜色,但是竖直方向相邻的两个格子不能是相同的颜色。现在是知道有多少种涂色方案,要求求出M
这是我RE的代码,心酸
#include<iostream>
#include<set>
#include<algorithm>
#include<math.h>
#include<map>
#include<stdio.h>
using namespace std;
typedef long long LL;
const LL mod=1e8+7;
int n,k,b,r;
int m,first_row;
const int N=510;
int x[N],y[N];
set<pair<int,int> > s;
LL mul_mod(LL a,LL b)
{
return a%mod*b%mod;
}
LL pow_mod(LL a,LL b)
{
LL ans=1;
while(b)
{
if(b&1) ans=ans%mod*a%mod;
a=a%mod*a%mod;
b>>=1;
}
return ans;
}
LL extgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0) {x=1;y=0;return a;}
LL d=extgcd(b,a%b,y,x);
y-=a/b*x;
}
LL inv(LL a)
{
LL x,y;
LL d=extgcd(a,mod,x,y);
if(d!=1) return -1;
else return (x+mod)%mod;
}
LL log_mod(LL a,LL b)
{
int m=ceil(sqrt(mod));
LL v=inv(pow_mod(a,m));
map<LL,LL> x;
x[1]=0; //a^0==1
for(int i=1;i<m;++i)
{
LL e=mul_mod(e,a);
if(!x.count(e)) x[e]=i;
}
for(int i=0;i<m;++i)
{
if(x.count(b))
return i*m+b;
b=mul_mod(b,v);
}
return -1;
}
int solve()
{
int c=0;//前m行可凃k种颜色格子
for(int i=0;i<b;++i)
{
if(x[i]!=m&&!s.count(make_pair(x[i]+1,y[i]))) ++c;
}
c+=n-first_row;
LL cnt=mul_mod(pow_mod(k,c),pow_mod(k-1,(LL)m*n-b-c));
if(cnt==r) return m;
c=0;
for(int i=0;i<b;++i)
if(x[i]==m) ++c;
cnt=mul_mod(cnt,pow_mod(k-1,n-c));
if(cnt==r) return m+1;
int p=pow_mod(k-1,n);
int v=inv(cnt);
return log_mod(p,mul_mod(r,v))+m+1;
}
int main()
{
int T;
scanf("%d",&T);
for(int kase=1;kase<=T;++kase)
{
scanf("%d%d%d%d",&n,&k,&b,&r);
s.clear();
m=1;
first_row=0;
for(int i=0;i<b;++i)
{
scanf("%d%d",&x[i],y[i]);
s.insert(make_pair(x[i],y[i]));
if(x[i]>m) m=x[i];
if(x[i]==1) ++first_row;
}
printf("%d\n",solve());
}
return 0;
}