2021/01/17訓練總結
技術標籤:演算法
文章目錄
前言
放假&開始訓練的第一天。寒假應該會每天都會訓練了,記錄一下一天收穫和寫一些題解。今天主要是寫寫了兩題數位dp然後把牛客小白賽31補完了。
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
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;
}