NO.1
Description
我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。
Input
输入一行,包含4个空格分开的正整数,依次为N,K,L和H。
Output
输出一个整数,为所求方案数。
Sample Input
2 2 2 4
Sample Output
3
HINT
样例解释
所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)
其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)
对于100%的数据,1≤N,K≤10^9,1≤L≤H≤10^9,H-L≤10^5
思路:容斥原理+快速幂
设g[i]表示以k*i为最小公约数的选数方案
那么对于当前的g[i]怎么求呢?
首先对于每一个i记一个left表示a/(k*i),right表示b/(k*i)
其实不算最大公约数大于k*i的情况下,就等于(right-left+1)^n-(right-left+1),其实这个题目上也有说明。。。用快速幂求即可
那么当前g[k*i]的真正答案就等于g[i]-g[i*2]-g[i*3]……g[k*i] (k*i<=maxn)
代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int mo=1000000007;
const int maxn=100000;
int n,k,a,b,l,r,g[200020];
int ksm(int x,int k)
{
int r=1;
while (k)
{
if (k&1) r=(1LL*r*x)% mo;
k>>=1;
x=(1LL*x*x)% mo;
}
return r;
}
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
scanf("%d%d%d%d",&n,&k,&a,&b);
l=a/k; r=b/k; if (a%k) l++;
for (int i=maxn;i>=1;i--)
{
int left=l/i,right=r/i;
if (l%i) left++;
if (l<=r)
{
g[i]=ksm(right-left+1,n);
g[i]=(g[i]-(right-left+1)+mo)%mo;
for (int j=i*2;j<=maxn;j+=i) g[i]=(g[i]-g[j]+mo)% mo;
}
}
if (l==1) g[1]=(g[1]+1)%mo;
printf("%d",g[1]);
return 0;
}
NO.2
Description
小H是个数学天才,在空闲的时候他喜欢发明各种各样的函数来自娱自乐。再过不久就是小H的同学,小小H的生日了。小H决定将自己刚刚发明的第19911026号函数当作生日礼物送给小小H,并把它命名为H函数。
其中H函数是这样定义的:H[A,B,C,D(x)] = (A * B^x + C) mod D。 在生日party上,小H将自己精心构想的礼物送上。同样精通数学的小小H对H函数自然非常感兴趣,以至于把其他礼物冷落一旁,专心研究H函数。 她想 如果给定一个正整数m,那么可以用F(A, B, C, D) 表示 H[A,B,C,D(x)]在 [1,m]取最小值时的x(x为正整数,且数据保证x值唯一) 善于思考的小小H很快想到一个问题来考考参加party的朋友们。 即给定n, m, {An}, {Bn}, {Cn}, {Dn},求出所有的F(Ai, Bi, Ci, Di)。
追求完美的小小H觉得答案念起来不够好听,为了能达到朗朗上口的效果,她偷偷修改了数据来满足F(Ai, Bi, Ci, Di) ≤ F(Ai+1, Bi+1, Ci+1, Di+1)。
Input
第1行包含2个正整数n, m。
第2 ~ n + 1行每行包含4个非负整数Ai, Bi, Ci, Di。
Output
输出文件包含n行,第i + 1行为一个整数,表示F(Ai, Bi, Ci, Di)。
保证F(Ai, Bi, Ci, Di) ≤ F(Ai+1, Bi+1, Ci+1, Di+1)。
Sample Input
3 5
373911025 1525760443 652804587 1767005941
120055457 159868670 59429374 196292251
1200581 955324 141748 2705431
Sample Output
1
2
4
Hint
温馨提示:在F(Ai+1,Bi+1,Ci+1,Di+1)中,(i+1)为下标 30%的数据满足:1 ≤ n, m ≤ 1000 60%的数据满足:1 ≤ n, m ≤ 10000 100%的数据满足: 1 ≤ n, m ≤ 100000,0
#include<cstdio>
#include<iostream>
using namespace std;
long long n,m,a[100001],b[100001],c[100001],d[100001],ans[100001];
long long ksm(long x,long y,long long z)
{
long long r=x,k=1;
while (y>0)
{
if (y&1==1) k=(k*r)%z;
y=y>>1;
r=(r*r)%z;
}
return k;
}
void doit(long long l,long long r,long head,long long tail)
{
if (l>r) return;
long long mid,min1,x,y,w;
w=0;
y=head;
mid=(l+r)/2;
x=ksm(b[mid],head-1,d[mid]);
min1=d[mid];
for (long long i=head;i<=tail;i++)
{
w=(((((a[mid]*x)%d[mid])*b[mid])%d[mid])+c[mid])%d[mid];
if (w<min1)
{
min1=w;
y=i;
}
x=(x*b[mid])%d[mid];
}
ans[mid]=y;
doit(l,mid-1,head,y);
doit(mid+1,r,y,tail);
}
int main()
{
scanf("%d%d",&n,&m);
for (long long i=1;i<=n;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
doit(1,n,1,m);
for (long long i=1;i<=n;i++) printf("%d
",ans[i]);
}
NO.3
Description
Alice总是会提出很多奇怪的问题,一天他让他的朋友Bob跟他一起研究一个奇怪的问题。问题是:[A,B]中有多少个数满足组成这个数的数字之和为S,另一个问题是[A,B]内满足这一要求最小的数是哪个?
编程帮Bob解决这个问题。
Input
输入包含三个整数A,B和S(1<=A<=B<10^15,1<=S<=135)。
Output
第一行输出一个整数表示满足条件的数的个数;
第二行输出最小的满足条件的数。
输入文件保证至少存在一个数。
Sample Input
输入1:
1 9 5
输入2:
1 100 10
输入3:
11111 99999 24
Sample Output
输出1:
1
5
输出2:
9
19
输出3:
5445
11499
思路:数位dp
设f[i][j][0/1][0/1]表示前i位,和为j,第一个0/1表示是否等于(0)或大于a(1),第二个0/1表示是否等于(0)或小于b(1)
四重循环枚举
对于每一个k和l,我们可以得出下一位枚举的范围
对于新加后的数,求出相应的k和l,进行转移
这样第一个问题就解决了
那么求最小值,我们还可以定义一个与上面差不多的方程g[i][j][0/1][0/1]
这样可以用同样的时间做两件事
最后的方案数=f[len+1][s][0][0]+f[len+1][s][0][1]+f[len+1][s][1][0]+f[len+1][s][1][1]
最小值=min(min(g[len+1][s][0][0],g[len+1][s][0][1]),min(g[len+1][s][1][0],g[len+1][s][1][1]))
代码:
uses math;
var a,b:int64;
i,j,k,l,s,z,len,l1,l2,k2,r1:longint;
x,y:array[0..16]of integer;
f,g:array[0..16,0..136,0..1,0..1]of int64;
procedure init;
var i:longint;
s1:string;
begin
readln(a,b,s);
str(b,s1);
len:=length(s1);
for i:=1 to len do y[i]:=ord(s1[i])-48;
str(a,s1);
while (length(s1)<len) do s1:='0'+s1;
for i:=1 to len do x[i]:=ord(s1[i])-48;
fillchar(g,sizeof(g),127);
f[1,0,0,0]:=1;
g[1,0,0,0]:=0;
end;
begin
init;
for i:=1 to len do
for j:=0 to s do
for k:=0 to 1 do
for l:=0 to 1 do
if f[i,j,k,l]>0 then
begin
if k=0 then l1:=x[i] else l1:=0;
if l=0 then r1:=y[i] else r1:=9;
for z:=l1 to r1 do
if (j+z<=s) then
begin
if z>x[i] then k2:=1 else k2:=k;
if z<y[i] then l2:=1 else l2:=l;
inc(f[i+1,j+z,k2,l2],f[i,j,k,l]);
if (g[i,j,k,l]*10+z)<g[i+1,j+z,k2,l2] then g[i+1,j+z,k2,l2]:=g[i,j,k,l]*10+z;
end;
end;
writeln(f[len+1,s,0,0]+f[len+1,s,0,1]+f[len+1,s,1,0]+f[len+1,s,1,1]);
writeln(min(min(min(g[len+1,s,0,0],g[len+1,s,0,1]),g[len+1,s,1,0]),g[len+1,s,1,1]));
end.
NO.4
Description
Alice又想到一个游戏:N个数每个数都在0到9之间,可以对每一个数进行加1操作,但这个加1比较特别,0-8加1后会相应变成1-9,但9加1后会变成0,给出N个数,进行M次操作,每次操作都会给出两个整数A和B(1<=A<=B<=N),要求输出第A个数到第B个数的和,然后把A到B之间的每一个数进行一次加1操作。
你能帮助Alice吗?
Input
输入文件第一行包含两个整数N和M(1<=N<=250000,1<=M<=100000);
第二行N个数字,每个数字都在0到9之间,中间没有空格,表示初始值。
接下来M行,每行包含两个整数A和B(1<=A<=B<=N)
Output
输出M行,每行表示对应区间的和,注意是先计算后操作。
Sample Input
输入1:
4 3
1234
1 4
1 4
1 4
输入2:
4 4
1234
1 1
1 2
1 3
1 4
输入3:
7 5
9081337
1 3
3 7
1 3
3 7
1 3
Sample Output
输出1:
10
14
18
输出2:
1
4
9
16
输出3:
17
23
1
19
5
思路:线段树
我们可以用tree[i][j]记录节点i拥有j数量的值
每次直接查询
再码个区间修改就好了
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#define N 250010
using namespace std;
char c[N];
int n,m;
int t[N<<2],tree[N<<2][10];
void ps(int x)
{
for(int i=0;i<10;++i) tree[x][i]=tree[x<<1][i]+tree[x<<1|1][i];
}
int sum(int x)
{
int ans=0;
for(int i=1;i<10;++i) ans+=i*tree[x][i];
return ans;
}
void rev(int* x,int k)
{
if(k>10) k%=10;
static int v[10];
memcpy(v,x+(10-k),k<<2);
memmove(x+k,x,(10-k)<<2);
memcpy(x,v,k<<2);
}
void pd(int m,int x)
{
if(t[x])
{
t[x<<1]+=t[x];
t[x<<1|1]+=t[x];
rev(tree[x<<1],t[x]);
rev(tree[x<<1|1],t[x]);
t[x]=0;
}
}
void build(int l,int r,int x)
{
if(l==r){ tree[x][c[l]-'0']++; return; }
int mid=l+r>>1;
build(l,mid,x<<1);
build(mid+1,r,x<<1|1);
ps(x);
}
void plu(int l,int r,int x,int head,int tail)
{
if(head<=l&&r<=tail)
{
t[x]++; rev(tree[x],1); return;
}
int mid=l+r>>1; pd(r-l+1,x);
if(head<=mid) plu(l,mid,x<<1,head,tail);
if(mid<tail) plu(mid+1,r,x<<1|1,head,tail);
ps(x);
}
int query(int l,int r,int x,int head,int tail)
{
if(head<=l&&r<=tail) return sum(x);
int mid=l+r>>1,ans=0;
pd(r-l+1,x);
if(head<=mid) ans+=query(l,mid,x<<1,head,tail);
if(mid<tail) ans+=query(mid+1,r,x<<1|1,head,tail);
return ans;
}
int main()
{
scanf("%d%d%s",&n,&m,c+1);
build(1,n,1);
for(int a,b,i=0;i<m;++i)
{
scanf("%d%d",&a,&b);
printf("%d
",query(1,n,1,a,b));
plu(1,n,1,a,b);
}
}