zoukankan      html  css  js  c++  java
  • 数位dp

    1. Codeforces 55D Beautiful numbers

    Problem : 询问一个区间内有多少个数,可以整除其每一位的数字。
    **Solution : **记录一下前若干位数字的lcm和前缀和,由于1~9的lcm是2520,所以前缀和可以对2520取模。对于合法的lcm压缩一下状态,就可以存下来了。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 20;
    const int SUM = 2520;
    
    int a[N];
    int index[SUM];
    long long dp[N][50][SUM];
    
    int gcd(int x, int y)
    {
    	return y ? gcd(y, x % y) : x;
    }
    
    int lcm(int x, int y)
    {
    	return x * y / gcd(x, y);
    }
    
    long long dfs(int pos, int prelcm, int presum, int limit, int leed)
    {
    	if (pos == 0)
    	{
    		if (presum % prelcm == 0 && !leed) return 1;
    		return 0;
    	}
    	if (!limit && !leed && ~dp[pos][index[prelcm]][presum]) return dp[pos][index[prelcm]][presum];
    	int u = limit ? a[pos] : 9;
    	long long ans = 0;
    	for (int i = 0; i <= u; ++i)
    	{
    		ans += dfs(pos - 1, (i == 0) ? prelcm : lcm(prelcm, i), (presum * 10 + i) % SUM, 
    					limit && i == u, leed && i == 0 );
    	}
    	if (!limit && !leed) dp[pos][index[prelcm]][presum] = ans;
    	return ans;
    }
    long long solve(long long x)
    {
    	int len = 0;
    	while (x)
    	{
    		a[++len] = x % 10;
    		x /= 10;
    	}
    	return dfs(len, 1, 0, 1, 1);
    }
    int main()
    {
    	cin.sync_with_stdio(0);
    	int tot;
    	index[0] = tot = 1;
    	for (int i = 1; i < SUM; ++i)
    	{
    		if (SUM % i == 0)
    		{
    			index[i] = ++tot;
    		}
    	}
    	memset(dp, -1, sizeof(dp));
    	int T; cin >> T;
    	for (int i = 0; i < T; ++i)
    	{
    		long long l, r;
    		cin >> l >> r;
    		cout << solve(r) - solve(l - 1) << endl;
    	}
    }
    

    2. HDU 4352 XHXJ'S LIS

    **Problem : ** 询问一个区间内有多少个数 ,最长严格上升序列长度为k。
    **Solution : ** 用类似于nlogn求最长不下降序列的思路,记录下每一位长度的最小数字。由于数字大小为0~9,且具有单调性质,直接用1<<10来表示每个数字出现过。在形成下一个状态时,找到大于等于当前数字的以为并替换。需要考虑如果有前导0并且当且数字是0的话是不记录状态的。

    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    long long dp[20][2048][11];
    int a[20];
    
    int get(int state)
    {
    	int len = 0;
    	while (state)
    	{
    		if (state % 2 == 1) len++;
    		state /= 2;
    	}
    	return len;
    }
    
    int next(int state, int num, int leed)
    {
    	if (leed && num == 0) return state;
    	for (int i = num; i <= 9; ++i)
    		if (state & (1 << i))
    			return (state ^ (1 << i)) | (1 << num);
    	return state | (1 << num);
    }
    
    long long dfs(int pos, int k, int state = 0, int limit = 1, int leed = 1)
    {
    	if (!limit && !leed && ~dp[pos][state][k]) return dp[pos][state][k];
    	if (pos == 0)
    	{
    		return get(state) == k;
    	}	
    	int u = limit ? a[pos] : 9;
    	long long ans = 0;
    	for (int i = 0; i <= u; ++i)
    	{
    		ans += dfs(pos - 1, k, next(state, i, leed), limit && i == u, leed && i == 0);
    	}
    	if (!limit && !leed) dp[pos][state][k] = ans;
    	return ans;
    }
    
    long long solve(long long x, int k)
    {
    	int len = 0;
    	while (x)
    	{
    		a[++len] = x % 10;
    		x /= 10;
    	}
    	return dfs(len, k);
    }	
    
    int main()
    {
    	memset(dp, -1, sizeof(dp));
    	cin.sync_with_stdio(0);
    	int T; cin >> T;
    	for (int cas = 1; cas <= T; ++cas)
    	{
    		long long l, r;
    		int k;
    		cin >> l >> r >> k;
    		cout << "Case #" << cas << ": " << solve(r, k) - solve(l - 1, k) << endl; 
    	}
    	/*for (int i = 123; i <= 321; ++i)
    	{
    		cout << i << " " << solve(i, 2) - solve(i - 1, 2) << endl;
    	}*/
    }
    

    3. HDU 4507 吉哥系列故事――恨7不成妻

    **Problem : **询问一个区间里满足以下性质的数字的平方和:没有一位是7, 数字和不是7的倍数, 这个数不是7的倍数
    **Solution : **用三个状态是否出现过7,数字之和,数位之和就可以判断是否是合法的数字。询问要求的是平方和,根据平方和公式(10a+b)^2可以得到只需记录平方和,和以及数量就可以递推得到下一位的平方和。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 20;
    const int mo =1e9 + 7;
    struct node
    {
    	long long num;
    	long long sum;
    	long long squre;
    	node(long long num_ = 0, long long sum_ = 0, long long squre_ = 0):num(num_), sum(sum_), squre(squre_){}
    };
    node dp[N][2][7][7];
    long long power[N];
    int a[N];
    void upd(long long &x, long long y)
    {
    	x = (x + y) % mo;
    }
    node dfs(int pos, int y7, int digit, int presum, int limit)
    {
    	if (pos == 0)
    	{
    		if (!y7 && digit && presum) return node(1, 0, 0); else return node(0, 0, 0);
    	}
    	if (!limit && ~dp[pos][y7][digit][presum].num) return dp[pos][y7][digit][presum];
    	int u = limit ? a[pos] : 9;
    	node ans;
    	for (int i = 0; i <= u; ++i)
    	{
    		node nxt = dfs(pos - 1, y7 || i == 7, (digit + i) % 7, (presum * 10 + i) % 7, limit && i == u);
    		upd(ans.num, nxt.num);	
    		upd(ans.sum, (nxt.sum + power[pos - 1] * i % mo * nxt.num) % mo);
    	   	upd(ans.squre, power[pos - 1] * power[pos - 1]  % mo * i * i % mo * nxt.num % mo);
    		upd(ans.squre, power[pos - 1] * 2 * i % mo * nxt.sum % mo);
    		upd(ans.squre, nxt.squre);
    	}
    	if (!limit) dp[pos][y7][digit][presum] = ans;
    	return ans;
    }
    
    long long solve(long long x)
    {
    	int len = 0;
    	while (x)
    	{
    		a[++len] = x % 10;
    		x /= 10;
    	}
    	return dfs(len, 0, 0, 0, 1).squre;
    }
    
    int main()
    {
    	power[0] = 1;
    	for (int i = 1; i < N; ++i) power[i] = power[i - 1] * 10 % mo;
    	cin.sync_with_stdio(0);
    	int T; cin >> T;
    	memset(dp, -1, sizeof(dp));
    	for (int cas = 0; cas < T; ++cas)
    	{
    		long long l, r; cin >> l >> r;
    		cout << ((solve(r) - solve(l - 1)) % mo + mo) % mo << endl;
    	}
    	return 0;
    }
    

    4. SPOJ BALNUM Balanced Numbers

    **Problem: ** 询问一个区间内有多少个数字满足:奇数数字出现偶数次,偶数数字出现奇数次。
    **Solution: ** 用一个三进制的数记录下每个数字出现的状态,0,1,2分别表示奇数次,偶数次和没出现过。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 22;
    const int M = 60000;
    
    typedef unsigned long long ull;
    
    ull dp[N][M];
    int a[N];
    int power[N];
    
    int change(int status, int num)
    {
    	int tot = status;
    	for (int i = 1; i <= num; i++) tot /= 3;
    	switch (tot % 3)
    	{
    	case 0:return status + power[num];break;
    	case 1:return status + power[num];break;
    	case 2:return status - power[num];break;
    	}
    }
    
    ull dfs(int pos, int status, int limit, int leed)
    {
    	if (pos == 0)
    	{
    		//cout << pos << " " << status << endl;
    		int tot = status;
    		for (int i = 0; i <= 9; ++i)
    		{
    			switch (tot % 3)
    			{
    			case 0:break;
    			case 1:if (i % 2 == 1) return 0;break;
    			case 2:if (i % 2 == 0) return 0;break;
    			}
    			tot /= 3;
    		}	
    		return 1;
    	}
    	if (!limit && !leed && ~dp[pos][status]) return dp[pos][status];
    	int u = limit ? a[pos] : 9;
    	ull ans = 0;
    	for (int i = 0; i <= u; ++i)
    	{
    		ans += dfs(pos - 1, (leed && i == 0) ? status : change(status,i), limit && i == u, leed && i == 0);
    	}
    	if (!limit && !leed) dp[pos][status] = ans;
    	return ans;
    }
    ull solve(ull x)
    {
    	int len = 0;
    	while (x)
    	{
    		a[++len] = x % 10;
    		x /= 10;
    	}
    	return dfs(len, 0, 1, 1);
    }
    int main()
    {
    	power[0] = 1;
    	for (int i = 1; i <= 9; ++i) power[i] = power[i - 1] * 3;
    	cin.sync_with_stdio(0);
    	int T; cin >> T;
    	memset(dp, -1, sizeof(dp));
    	for (int i = 1; i <= T; i++)
    	{
    		ull l, r;
    		cin >> l >> r;
    		cout << solve(r) - solve(l - 1) << endl;
    	}	
    	return 0;
    }
    

    5. HDU 4734 F(x)

    **Problem: **定义F(x) = An * 2^(n-1) + ... + A 2 * 2 + A 1 * 1.询问0~B内有多少个数满足f(x) <= f(A)。 10000组数据。
    **Solution: **由于有多组数据,因此需要考虑记录下与输入无关的状态,来优化程序速度。倒着来存储状态,记录下当前数值距离0还差多少,从而与A无关。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 12;
    const int M = 10008;
    
    int dp[N][M];
    int a[N];
    
    int dfs(int pos, int presum, int limit)
    {
    	if (presum < 0) return 0;
    	if (pos == 0) return 1;
    	if (!limit && ~dp[pos][presum]) return dp[pos][presum];
    	int u = limit ? a[pos] : 9;
    	int ans = 0;
    	for (int i = 0; i <= u; ++i)
    	{
    		ans += dfs(pos - 1, presum - (1 << pos - 1) * i, limit && i == u);
    	}
    	if (!limit) dp[pos][presum] = ans;
    	return ans;
    }
    
    int solve(int A, int B)
    {
    	int len = 0, sum = 0;
    	while (A)
    	{
    		a[++len] = A % 10;
    		A /= 10;
    	}
    	for (int i = 1; i <= len; ++i) sum = sum + a[i] * (1 << i - 1);
    	len = 0;
    	while (B)
    	{
    		a[++len] = B % 10;
    		B /= 10;
    	}
    	return dfs(len, sum, 1);
    }
    
    int main()
    {
    	cin.sync_with_stdio(0);
    	int T; cin >> T;
    	memset(dp, -1 ,sizeof(dp));
    	for (int i = 1; i <= T; ++i)
    	{
    		int A, B; cin >> A >> B;
    		cout << "Case #" << i << ": " << solve(A, B) << endl; 
    	}
    	return 0;
    }
    
  • 相关阅读:
    HashMap按键排序和按值排序
    LeetCode 91. Decode Ways
    LeetCode 459. Repeated Substring Pattern
    JVM
    LeetCode 385. Mini Parse
    LeetCode 319. Bulb Switcher
    LeetCode 343. Integer Break
    LeetCode 397. Integer Replacement
    LeetCode 3. Longest Substring Without Repeating Characters
    linux-网络数据包抓取-tcpdump
  • 原文地址:https://www.cnblogs.com/rpSebastian/p/7183938.html
Copyright © 2011-2022 走看看