1. 程式人生 > 其它 >2021/01/17訓練總結

2021/01/17訓練總結

技術標籤:演算法

文章目錄

前言

放假&開始訓練的第一天。寒假應該會每天都會訓練了,記錄一下一天收穫和寫一些題解。今天主要是寫寫了兩題數位dp然後把牛客小白賽31補完了。

P4317 花神的數論題

題目連結:P4317 花神的數論題
題目大意:
在這裡插入圖片描述
資料範圍: 1 ≤ N ≤ 1 0 15 1\le N\le10^{15} 1N1015
題解:我們考慮去計算 1 − N 1-N 1N 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} 1ab1018
題解:很明顯的數位dp,基本上數位dp都是要求 1 − n 1-n 1n中有多少個數滿足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(a1),我們考慮 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 1a,x109
題解:題目一看還以為數位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的個數} 2a0。我們現在再加上限制,假設x是 10101010 10101010 10101010,對於我們假設當前遍歷選擇的b字首都和x相同,例如我們現在遍歷到了第4位,那麼我們選擇的b字首都是 1010 1010 1010。我們考慮x每一個為1的位,我們新增答案,我們可以假設b在當前位置為0,那麼我們後面的位就相當於沒有x的限制了,因為無論我們怎麼選擇都不會超過x。所以新增 2 a 剩 餘 二 進 制 數 中 0 的 個 數 2^{a剩餘二進位制數中0的個數} 2a0。這樣我們遍歷了 0 ∼ x − 1 0\sim {x-1} 0x1,再加上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 1t103, 1 ≤ a , b ≤ 1 0 6 1 \le a, b \le 10^6 1a,b106
題解:其實之前就遇見過類似的,不過還是沒記住這種題目的套路,一般看見x*y這種式子就要想著如何去進行因式分解,這題就是兩邊加上 a ∗ b a*b ab得到 a ∗ x + b ∗ y + a ∗ b = x ∗ y + a ∗ b a*x+b*y+a*b=x*y+a*b ax+by+ab=xy+ab,移項得到 a ∗ b = x ∗ y − a ∗ x − b ∗ y + a ∗ b a*b=x*y-a*x-b*y+a*b ab=xyaxby+ab a ∗ b = ( x − a ) ∗ ( y − b ) a*b=(x-a)*(y-b) ab=(xa)(yb)答案就是求 a ∗ b a*b ab的因子數。暴力 ( 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;
}