【SWTR-02】Sweet Round 02 T4 Magical Gates
欢迎爆踩
你感受过明天就要比赛却在比赛前一天发现有相似类型的题目的绝望吗?
双倍经验,如果你过了这道题目就一定能
可以将本题看成该题的超级加强版(但是我们出题的时候并不知道有该题的存在)
本题主要考察数位
暴力分
-
下文中所有的
n 都代表该测试点的数据范围(可以理解为n=\max r ) -
下文中所有的
\log 以2 为底 -
下文中,
\binom{i}{j} 表示从j 个不同元素中选出i 个元素的方案数,即我们通常说的组合数,也可表示为C_{j}^{i} ,计算公式为\frac{j!}{i!(j-i)!}
将题目所给代码直接复制
发现题目中所给 get 函数就是求:一个数在二进制下有多少个
对
然后暴力回答询问即可
时间复杂度:
前置知识:乘法逆元和有理数取余,如果不会请先移步
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#pragma GCC optimize(3)
int t,d[10000005],q[10000005];
ll k[10000005],p,n;
ll ksm(ll a,ll b,ll p){ll ans=1;while(b){if(b&1)ans=(ans*a)%p;b>>=1,a=(a*a)%p;}return ans;}
int lowbit(int x){return x&-x;}
int main()
{
cin>>t>>p>>n;
for(int i=1;i<=1e7;i++)
d[i]=d[i-lowbit(i)]+1;
k[0]=1;
for(int i=1;i<=1e7;i++)
q[i]=q[i-1]+d[i],k[i]=k[i-1]*d[i]%p;
while(t--){
int l,r;
cin>>l>>r;
cout<<q[r]-q[l-1]<<" "<<k[r]*ksm(k[l-1],p-2,p)%p<<endl;//费马小定理求逆元
}
return 0;
}
前置知识:高精,找规律,欧拉定理,如果不会请先移步
找规律(或者推出结论),当
可以
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll t,p,p2,n,C[3333][3333];
string l,r;
int x[1005];
ll ksm(ll a,ll b){ll s=1,m=a;while(b){if(b&1)s=s*m%p;m=m*m%p;b>>=1;}return s;}
int main()
{
cin>>t>>p>>n,p2=p-1;
int N=n<15?70:3332;
for(int i=1;i<N;i++)
for(int j=0;j<=i;j++)
if(j==0||i==j)C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%p2;
for(int i=0;i<t;i++){
cin>>l>>r;
int top=r.size(),bit=0;
for(int j=0;j<r.size();j++)
x[j]=r[r.size()-j-1]-'0';
while(top){
bit++;
int tmp=0;
for(int j=top-1;j>=0;j--)
tmp=tmp*10+x[j],x[j]=tmp/2,tmp%=2;
if(!x[top-1])top--;
}
bit--;
int mul=1;
for(int j=1;j<=bit;j++)
mul=mul*ksm(j,C[bit][j])%p;
cout<<(bit*ksm(2,bit-1)+1)%p<<" "<<mul<<endl;
}
return 0;
}
结合
代码就不贴了
前置知识:高精,欧拉定理,数位
- 在下文中,
x_{(2)} 表示x 在二进制下表示的数
我们考虑数位
如何计算区间
-
设
x_{(2)} 一共有k 位,从左往右遍历x_{(2)} 的每一位(只有可能为0 或1 ) -
如果该位为
0 :跳过,不考虑 -
如果该位为
1 :将该位为0 ,右边的位为任意值 的情况讨论到答案中,这样保证了不会 少计算/多计算/重复计算,且在接下来的讨论中,将该位的值看作为1
即设当前考虑到第
对于所有
- 遍历过所有位之后,还要将
x_{(2)} 本身的答案算进去,即dp_{cnt} 加1
Q2:
为什么我按照
因为求积的
同样的,上式
为什么我按照
可以先
千万别小看这个
这个算法的时间复杂度是多少?
一次
说了这么多废话,赶紧把代码拿出来
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3333;
ll ksm(ll a,ll b,ll p){ll ans=1;while(b){if(b&1)ans=(ans*a)%p;b>>=1,a=(a*a)%p;}return ans;}
ll t,p,p2,n,k,add[N][N],mult[N][N],prod[N],sum[N];
bool bit[N];//二进制
string l,r;
void change(string s,bool minus)//将字符串转成二进制数,minus 为是否减 1(计算l时需要)
{
k=0;
int l=s.size()-1,p[N],high=l;
for(int i=l;i>=0;i--)p[i]=s[l-i]-'0';//高精度
if(minus){
p[0]--;
int pos=0;
while(p[pos]<0)p[pos+1]--,p[pos]+=10,pos++;
if(!p[high])high--;
}
while(high>=0){
k++;
int res=0;
for(int i=high;i>=0;i--){
res=res*10+p[i];
p[i]=res/2;
res%=2;
}
bit[k]=res;
if(!p[high])high--;
}
}
void solve()//一组数据
{
memset(sum,0,sizeof(sum));
memset(prod,0,sizeof(prod));
cin>>l>>r;
change(r,0);
int cnt=0;
for(int j=k;j>0;j--)
if(bit[j]){
for(int i=0;i<j;i++)
sum[i+cnt]=(sum[i+cnt]+add[j-1][i])%p,
prod[i+cnt]=(prod[i+cnt]+mult[j-1][i])%p2;
cnt++;
}
sum[cnt]++,prod[cnt]++;//计算[1,r]
change(l,1),cnt=0;
for(int j=k;j>0;j--)
if(bit[j]){
for(int i=0;i<j;i++)
sum[i+cnt]=(sum[i+cnt]-add[j-1][i]+p)%p,
prod[i+cnt]=(prod[i+cnt]-mult[j-1][i]+p2)%p2;
cnt++;
}
sum[cnt]=(sum[cnt]-1+p)%p,prod[cnt]=(prod[cnt]-1+p2)%p2;//减去[1,l)
}
void init()
{
cin>>t>>p>>n,p2=p-1;
int lim;
if(n<18)lim=64;
else if(n<21)lim=333;
else lim=3333;//避免浪费时间和空间
add[0][0]=mult[0][0]=1;
for(int i=1;i<lim;i++){
add[i][0]=mult[i][0]=1;
for(int j=1;j<=i;j++){
add[i][j]=(add[i-1][j]+add[i-1][j-1])%p;
mult[i][j]=(mult[i-1][j]+mult[i-1][j-1])%p2;
}
}//预处理出组合数
}
int main()
{
init();
for(ll i=1;i<=t;++i){
solve();
ll ans=0;
for(ll j=1;j<N;j++)
ans=(ans+sum[j]*j)%p;
cout<<ans<<" ";
ans=1;
for(ll j=1;j<N;j++)
ans=(ans*ksm(j,prod[j],p))%p;//这里是对 p 取模,因为已经在算最终的答案了
cout<<ans<<endl;
}
return 0;
}
当然,解法并不唯一,如果有更好的思路也欢迎大家在题解区发布!
如果题解有误请私信我,或在右侧评论区指出