【2019 上海网络赛】

L.Digtal Sum.水题,由于b很小,预处理掉所有的数字即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int dp[11][N];
int sum[11][N];
void init()
{
	for (int ba = 2; ba <= 10; ba++)
	{
		for (int i = 1; i <N; i++)
		{
			int cur = i;
			while (cur>=ba)
			{
				dp[ba][i] += (cur % ba);
				cur/= ba;
			}
			dp[ba][i] += cur;
		}
	}
	for (int ba = 2; ba <= 10; ba++)
	{
		for (int i = 1; i <N; i++)
		{
			sum[ba][i]=sum[ba][i-1]+dp[ba][i];
		}
	}
}

int main()
{
	init();
	int T;
	scanf("%d", &T);
	for (int ca = 1; ca <= T; ca++)
	{
		int n, b;
		scanf("%d%d", &n, &b);
		printf("Case #%d: %d\n", ca,sum[b][n]);
	}
	return 0;
}

B. Light bulbs。题意:n个灯初始都是关闭状态,现在每次选定一个区间把这个区间的状态反转(开-->关,关-->开)。问m次操作以后有多少个灯还是开着的。这个题目乍一看就是BIT的区间修改,点查询,复杂度O(T*M*logN),但是TLE。采用差分来做一下,其实和区间的离散化思想是一样的。把操作区间的真区间完全保存下来(啥意思?就是如果我操作[l,r],那么我存的区间就是[l,r+1)这个左闭右开的区间,区间一定要这样存),然后排序做一下区间差分,求个前缀和就是答案。复杂度O(T*M*logM)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int a[N];
int main()
{
	int T;
	scanf("%d", &T);
	for(int ca=1;ca<=T;ca++)
	{
		int n, m;
		scanf("%d%d", &n, &m);
		int cnt = 0;
		for (int i = 0; i < m; i++)
		{
			int l, r;
			scanf("%d%d", &l, &r);
			a[cnt++] = l;
			a[cnt++] = r+1;
		}
		sort(a, a + cnt);
		int ans = 0;
		for (int i = 1; i < cnt; i += 2)
		{
			ans += a[i] - a[i - 1];
		}
		printf("Case #%d: %d\n", ca, ans);
	}

}

J.Stone Game.题意:给长度为n的数组,每个数的范围是[1,500].问有多少种方法能够在这个集合里面选取一个子集S`,使得:

sum表示这个集合元素和。

首先,把集合元素排序,从大到小。然后遍历,取当前遍历到的这个元素作为取出来的集合的最小值。那么也就是说要在他的前边选取和为x的元素,满足条件。根据条件,可以得到不等式约束:

                                                  (1)              x+a[i]>sum-x-a[i]

                                                  (2)              x+a[i]]+a[i]<sum-x+a[i]

可以解出来凑出来的上下界:

                                                         \frac{sum+1}{2}-a_i\leq x \leq\frac{sum+a_i}{2}-a_i

所以搞一个记忆化数组:dp[i]表示凑成i的方案数,然后每次加入首先统计答案,再更新dp数组。完事。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 300 * 500 + 10;
ll dp[N];
int a[510];
const int mod = 1e9 + 7;
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n;
		scanf("%d", &n);
		int sum = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
			sum += a[i];
		}
		sort(a + 1, a + n + 1);
		memset(dp, 0, sizeof(dp));
		dp[0] = 1;
		ll ans = 0;
		for (int i = n; i >= 1; i--)
		{
			int mmin = (sum + 1) / 2 - a[i];
			int mmax = (sum + a[i]) / 2 - a[i];
			for (int j = mmin; j <= mmax; j++)
			{
				if (j < 0)continue;
				ans = (ans + dp[j]) % mod;
			}
			for (int j = sum; j >= a[i]; j--)
			{
				dp[j] += dp[j - a[i]];
				dp[j] %= mod;
			}
		}
		cout << ans%mod << endl;
	}
}

D. Counting Sequences I。n个数的和等于n个数的乘积,似乎没啥好的解法,搜索剪枝,然后打个表交上去。emmmm,没啥兴趣写。

E.Counting Sequences II.母函数裸题,之前的博客写了这题了。题解

C.Triple.题意:给三个数组,问能够组成多少个不同的三角形。感觉题目起源于HDU 4609。HDU 4609是说给一个数组,问你能够组成多少个三角形,那个题目是预处理(a+b)的和,然后对于每个c找一下即可。预处理a+b的时候采用FFT加速。

这个题目其实只是把一个数组变成了三个数组,都OK,只不过以前预处理的时候是这个数组卷积他自己,现在是两个数组卷积。很直观的分析就是首先假设最大的边在C数组里,然后A*B,再去掉不合法的。同理假设最长边在A,B中再搞两次。但是这会漏掉一种等边的时候,再特判一下即可。emmm,有点复杂,不如直接计算不合法的,然后总数剪掉这些不合法的也行。然后就完了。??为什么没有代码?(我口胡的,还没写。FFT写起来好累,嘤~)

行吧,先到这里了。还有一个数位DP没有补,歇会继续补,顺便把FFt写了。

发布了370 篇原创文章 · 获赞 48 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/102534384
今日推荐