前言
放假&开始训练的第一天。寒假应该会每天都会训练了,记录一下一天收获和写一些题解,现在洛谷1160题,看寒假结束能有多少吧。今天主要是写写了两题数位dp然后把牛客小白赛31补完了。(我会记住上海站面对数位dp时的无力)
P4317 花神的数论题
题目链接:P4317 花神的数论题
题目大意:
数据范围:
1
≤
N
≤
1
0
15
1\le N\le10^{15}
1≤N≤1015
题解:我们考虑去计算
1
−
N
1-N
1−N中
s
u
m
(
i
)
=
x
sum(i)=x
sum(i)=x的个数,然后累乘起来就行了。现在问题就是如何求出
s
u
m
(
i
)
=
x
sum(i)=x
sum(i)=x的
i
i
i的个数。我们采取记忆化搜索(数位dp)的方法来进行。重要讲解一下
d
f
s
dfs
dfs函数,参数里面
c
u
r
cur
cur表示剩余位数,
u
p
up
up表示是否顶着上界,
n
o
w
now
now表示现在已经遍历出来的1的个数,
q
u
e
que
que表示要求的1的个数。
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
const ll N = 200000 + 10;
const int mod = 10000007;
#define int long long
ll fpow(int x, int y)
{
int ans = 1;
while (y)
{
if (y & 1)ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod; y >>= 1;
}
return ans;
}
int w[55];
int f[55][2][55][55];
int dfs(int cur, int up, int now, int que)
{
if (!cur)
{
return now == que;
}
if (~f[cur][up][now][que])return f[cur][up][now][que];
int tp = up ? w[cur] : 1;
int ans = 0;
for (int i = 0; i <= tp; i++)
{
ans = (ans + dfs(cur - 1, up && i == tp, now + (i == 1), que));
}
return f[cur][up][now][que] = ans;
}
signed main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
ll n;
read(n);
while (n)
w[++w[0]] = n & 1, n >>= 1;
ll ans = 1;
memset(f, -1, sizeof(f));
for (int i = 1; i <= w[0]; i++)
{
ans = ans * fpow(i, dfs(w[0], 1, 0, i)) % mod;
}
printf("%lld\n", ans);
return 0;
}
P4127 [AHOI2009]同类分布
题目链接:P4127 [AHOI2009]同类分布
题目大意:
数据范围:
1
≤
a
≤
b
≤
1
0
18
1≤a≤b≤10^{18}
1≤a≤b≤1018
题解:很明显的数位dp,基本上数位dp都是要求
1
−
n
1-n
1−n中有多少个数满足xx要求,我们用
f
i
n
d
(
n
)
find(n)
find(n)函数来代替,这题答案就是
f
i
n
d
(
b
)
−
f
i
n
d
(
a
−
1
)
find(b)-find(a-1)
find(b)−find(a−1),我们考虑
f
i
n
d
(
n
)
find(n)
find(n)这个函数要怎么求,一般来说数位dp的搜索函数都要有这几个参数,
c
u
r
cur
cur表示还剩多少位,
l
i
m
i
t
limit
limit表示是否顶着上界,然后有几个参数用来判断是否满足条件和剪枝。这题里面
s
u
m
sum
sum表示搜索的数的数位和是多少,
r
e
m
rem
rem表示搜索的数模
m
o
d
mod
mod后的值,其中
m
o
d
mod
mod表示我们要求的数位和。
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
const ll N = 200 + 10;
#define int long long
ll f[20][163][163], w[N];
int mod;
ll dfs(int cur, int sum, int rem, int limit)
{
if (!cur)
{
return (mod == sum) && (rem == 0);
}
if (!limit && ~f[cur][sum][rem])return f[cur][sum][rem];
if (sum > mod || sum + 9 * cur < mod)return 0;
int tp = limit ? w[cur] : 9;
ll ans = 0;
for (int i = 0; i <= tp && sum + i <= mod; i++)
{
ans += dfs(cur - 1, sum + i, (rem * 10 + i) % mod, limit && (i == tp));
}
return f[cur][sum][rem] = ans;
}
ll find(ll x)
{
w[0] = 0;
while (x)
w[++w[0]] = x % 10, x /= 10;
ll ans = 0;
for (mod = 1; mod <= w[0] * 9; mod++)
{
memset(f, -1, sizeof(f));
ans += dfs(w[0], 0, 0, 1);
}
return ans;
}
signed main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
ll a, b;
read(a), read(b);
printf("%lld\n", find(b) - find(a - 1));
return 0;
}
小白赛31A
题目链接:A|B
题目大意:
数据范围:
t
<
=
1
e
5
t<=1e5
t<=1e5,
1
≤
a
,
x
≤
1
0
9
1≤a,x≤10^9
1≤a,x≤109
题解:题目一看还以为数位dp,在看
t
t
t是
1
e
5
1e5
1e5数量级,明显排除数位dp。我们来考虑,没有第一个限制的情况,假设a是
10101010
10101010
10101010,b只要对于a的二进制为1的位填上0即可,而a的二进制为0的位,b可以选择0|1,所以答案就是
2
a
二
进
制
数
中
0
的
个
数
2^{a二进制数中0的个数}
2a二进制数中0的个数。我们现在再加上限制,假设x是
10101010
10101010
10101010,对于我们假设当前遍历选择的b前缀都和x相同,例如我们现在遍历到了第4位,那么我们选择的b前缀都是
1010
1010
1010。我们考虑x每一个为1的位,我们新增答案,我们可以假设b在当前位置为0,那么我们后面的位就相当于没有x的限制了,因为无论我们怎么选择都不会超过x。所以新增
2
a
剩
余
二
进
制
数
中
0
的
个
数
2^{a剩余二进制数中0的个数}
2a剩余二进制数中0的个数。这样我们遍历了
0
∼
x
−
1
0\sim {x-1}
0∼x−1,再加上x并且去除0,就可以得到答案
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
//#define int long long
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
ll t, a, x;
signed main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
read(t);
while (t--)
{
read(a), read(x);
ll ans = 0;
for (int i = 31; i >= 0; i--)
{
if (((1ll << i) & x) == 0)continue;
int c = 0;
for (int j = i - 1; j >= 0; j--)
if (((1ll << j) & a)==0)c++;
ans += (1ll << c);
if (((1ll << i) & a))break;
}
if ((a | x) == (a + x))ans++;
printf("%lld\n", ans - 1);
}
return 0;
}
小白赛31E 解方程
题目链接:解方程
题目大意:
数据范围:
1
≤
t
≤
1
0
3
1≤t≤10^3
1≤t≤103,
1
≤
a
,
b
≤
1
0
6
1 \le a, b \le 10^6
1≤a,b≤106
题解:其实之前就遇见过类似的,不过还是没记住这种题目的套路,一般看见x*y这种式子就要想着如何去进行因式分解,这题就是两边加上
a
∗
b
a*b
a∗b得到
a
∗
x
+
b
∗
y
+
a
∗
b
=
x
∗
y
+
a
∗
b
a*x+b*y+a*b=x*y+a*b
a∗x+b∗y+a∗b=x∗y+a∗b,移项得到
a
∗
b
=
x
∗
y
−
a
∗
x
−
b
∗
y
+
a
∗
b
a*b=x*y-a*x-b*y+a*b
a∗b=x∗y−a∗x−b∗y+a∗b即
a
∗
b
=
(
x
−
a
)
∗
(
y
−
b
)
a*b=(x-a)*(y-b)
a∗b=(x−a)∗(y−b)答案就是求
a
∗
b
a*b
a∗b的因子数。暴力
(
O
n
)
(O\sqrt{n})
(On)求解肯定是不行的,拿唯一分解定理搞一下就行
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
const ll N = 2000000 + 10;
const int mod = 1e9 + 7;
int t, a, b;
int prime[N];
bool fprime[N];
void init(int n)
{
for (int i = 2; i <= n; i++)
{
if (!fprime[i])prime[++prime[0]] = i;
for (int j = 1; j <= prime[0] && i * prime[j] <= n; j++)
{
fprime[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
int solve(ll x)
{
int ans = 1;
for (int j = 1; j <= prime[0] && prime[j] * prime[j] <= x; j++)
{
int cnt = 0;
while (x % prime[j] == 0)x /= prime[j], cnt++;
ans *= cnt + 1;
}
if (x != 1)ans *= 2;
return ans;
}
int main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
init(1000000);
read(t);
while (t--)
{
read(a), read(b);
printf("%d\n", solve(a*b));
}
return 0;
}