国庆七连测(六)计数

34 篇文章 0 订阅
24 篇文章 1 订阅
	给出一个数n,根据该数建图,从每个点到该点的倍数(包括自己和0)连一条单向边
	然后给出三个数,q1,q2,q3,求出
	1.从1到q1所有长度为2的点
	2.从1到0中长度小于q2的路径个数
	3.从1到0中长度小于q3且不经过重复点的路径个数
	4.3中所有路径上的编号和的和
	这道题看上去是道数论题,实际上是dp题

对于第一问,我们可以发现,从1可以到任何数,而到q1的只能是q1的因数,所以直接暴力求因数个数,不讲了
对于第二问我们可以dp首先对于小于1000的数据可以暴力dp,更大的数据我们可以根据第三问的答案来求第二问。
假设我们知道了一条路径(不走重复点)
设路径上第i各节点在最后的路径上出现了xi次,那么就是x1+x2+x3+…+xk<=q2的正整数解个数,用组合数可以知道答案是 c(k,q2)
对于第三问,我们发现,从i走到的点只有i,2i,3i,…[n/i]i
然后我们对这些点重新标号为1,2,3,…[n/i],然后路径的条数是一样的!!!
唉,Hash_table使人愁
Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2e5;
const int mod  =1e9+7;
const int inv2 =5e8+4;
int n,q1,q2,q3,mxc;
int inv[233],C[233];
LL ans1,ans2,ans3,ans4;
void solve1(){
	if(q1==0) ans1=n;
	else{
		int i;ans1=0;
		for(i=1;i*i<q1;i++)
			if(q1%i==0) ans1+=2;
		if(i*i==q1) ans1++;
	}
}
struct hash_table{
	static const int mod=2333347;
	int hash(int _key1,int _key2){
		return (_key1+(_key2<<20))%mod;
	}
	int hd[mod],key1[maxn],key2[maxn],val3[maxn],val4[maxn],nxt[maxn],tl;
	int insert(int _key1,int _key2,int _val3,int _val4){
		int h=hash(_key1,_key2);
		++tl;key1[tl]=_key1;key2[tl]=_key2;
		val3[tl]=_val3;val4[tl]=_val4;
		nxt[tl]=hd[h];hd[h]=tl;
		return tl;
	}
	int findid(int _key1,int _key2){
		int h=hash(_key1,_key2);
		int cnt=1;
		for(int i=hd[h];i;i=nxt[i],++cnt){
			if(mxc<cnt) mxc=cnt;
			if(key1[i]==_key1&&key2[i]==_key2)
				return i;
		}
		return -1;
	}
}dp;
int dfs(int n,int d){
	int ret=dp.findid(n,d) ;
	if(ret!=-1) return ret;//remember
	if(d==1) return dp.insert(n,d,1,1) ;
	if(n==1) return dp.insert(n,d,0,0) ;
	int dp3=0,dp4=0;
	for(int l=2,r=-1;l<=n;l=r+1){
		int t=n/l;r=n/t;
		int id=dfs(t,d-1);
		dp3=(dp3+1LL*dp.val3[id]*(r-l+1))%mod;
		dp4=(dp4+1LL*dp.val4[id]*(l+r)%mod*(r-l+1)%mod*inv2)%mod;
	}
	dp4+=dp3;if(dp4>=mod) dp4-=mod;
	return dp.insert(n,d,dp3,dp4);
}
void solve34(){
	for(int i=1;i<=q3&&i<=25;i++){
		int id=dfs(n,i);//at position(n),len is i
		ans3+=dp.val3[id];
		ans4+=dp.val4[id];
	}
	ans3%=mod,ans4%=mod;
}
void solve2(){
	inv[1]=C[0]=1;
	for(int i=2;i<=25;i++)
		inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=25;i++)
		C[i]=1LL*C[i-1]*(q2-i+1)%mod*inv[i]%mod;
	for(int i=1;i<=q2&&i<=25;i++){
		int id=dfs(n,i);
		ans2=(ans2+1LL*dp.val3[id]*C[i])%mod;
	}
}
int main(){
	scanf("%d%d%d%d",&n,&q1,&q2,&q3);
	solve1(),solve34(),solve2();
	printf("%lld %lld %lld %lld\n",ans1,ans2,ans3,ans4);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值