0
点赞
收藏
分享

微信扫一扫

【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`,使得:

【2019 上海网络赛】_预处理

sum表示这个集合元素和。

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

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

                                                  (2)              【2019 上海网络赛】_数组_03

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

                                                         【2019 上海网络赛】_预处理_04

所以搞一个记忆化数组: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写了。


举报

相关推荐

0 条评论