P1450 [HAOI2008] 硬币购物
完全背包+容斥原理
const int N = 1e5 + 1;
int n, s, b[4], c[4];
ll dp[N + 10], ans;
//x 硬币序号,sgn 容斥系数,sum 当前值的总和
void dfs(int x, int sgn, ll sum) {
if(x == 4) {
if(sum > s) return;
ans += sgn * dp[s - sum];
}else {
//合法
dfs(x + 1, sgn, sum);
//不合法
dfs(x + 1, -sgn, sum + 1LL * (b[x] + 1) * c[x]);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
for (int i = 0; i < 4; i++) {
cin >> c[i];
}
cin >> n;
dp[0] = 1;
for (int i = 0; i < 4; i++) { //完全背包--我们可以无限制的放硬币
for (int j = c[i]; j <= N; j++) {
dp[j] += dp[j - c[i]];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < 4; j++) {
cin >> b[j];
}
cin >> s;
ans = 0;
dfs(0, 1, 0);
cout << ans << "\n";
}
return 0;
}
河南萌新赛8 B
const int N = 5e5+10;
const int mod = 1e9 + 7;
int s1[N], s2[N]; //s1[i]表示i被多少条线段覆盖,s2[i]表示i被多少组线段覆盖
void solve() {
int n;
cin >> n;
vector<int> p(n + 1); //p[i]表示被i组线段覆盖产生的贡献,即2 ^ i
p[0] = 1; //没有被任意一组线段覆盖,即为1
for (int i = 1; i <= n; i++) {
p[i] = p[i - 1] * 2 % mod;
int l1, r1, l2, r2, l, r;
cin >> l1 >> r1 >> l2 >> r2;
l = max(l1, l2), r = min(r1, r2);
s1[l1]++;
s1[r1 + 1]--;
s1[l2]++;
s1[r2 + 1]--;
if(l <= r) { //计算被该组覆盖的点
s2[l]++;
s2[r + 1]--;
}
}
int p1 = 0, p2 = 0;
ll ans = 0;
for (int i = 1; i <= 5e5 + 1; i++) {
// cout << "i : " << i << ' ' << s1[i] << ' ' << s2[i] << endl;
p1 += s1[i]; //前缀和
p2 += s2[i];
if(p1 - p2 == n) { //p2在p1中加了需要减去,若全部覆盖则添加答案
ans += p[p2];
}
}
cout << ans % mod << endl;
}
cf833 B.Diverse Substrings
题目大意:给你一个长度为n的由'0'~'9'组成的字符串(n<=1e5),如果非空数字字符串中每个字符
的出现次数不超过其中不同字符的个数,那么该字符串就是多样的。 问在该串的n*(n+1)/2个子串中,
有多少个多样的。
由题意知,最大相同字符数量不能超过不同字符数量,而最多的字符数量只有10个,所有多样子串
中的每一个字符出现数量都不能超过10,所有多样子串的最大长度为100,暴力枚举O(100*n)。
void solve() {
int n;
cin >> n;
string s;
cin >> s;
int ans = 0;
for (int i = 0; i < n; i++) {
int vis[10] = {0}, max_cnt = 0, cnt = 0;
for (int j = i; j < n && (++vis[s[j] - '0'] <= 10); j++) {
max_cnt = max(max_cnt, vis[s[j] - '0']);
if(vis[s[j] - '0'] == 1) cnt++;
ans += (max_cnt <= cnt);
}
}
cout << ans << endl;
}
cf833 C.Zero-Sum Prefixes
题目大意:给你一个长度为n的数组a (n <= 2e5, -1e9 <= a[i] <= 1e9), 可以把
数组中为0的元素改为任意整数,求最终数组有多少个前缀之和为0。
先把原数组进行前缀和,对于a[i]==0,找到i到下一个0或到数组末尾出现次数最多的元素x,将a[i]
改为-x,第一个0前面需要单独计算答案。
void solve() {
int n;
cin >> n;
vector<ll> a(n), sum(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
sum[i] = a[i];
if(i) sum[i] += sum[i - 1];
}
map<ll, int> mp;
int ans = 0;
for (int i = n - 1; i >= 0; i--) {
mp[sum[i]]++;
if(a[i] == 0 && !mp.empty()) {
int cnt = 1;
for (auto y : mp) {
if(y.second >= cnt) {
cnt = y.second;
}
}
//cout << cnt << endl;
ans += cnt;
mp.clear();
}
}
for (int i = 0; i < n; i++) {
if(!a[i]) break;
ans += (sum[i] == 0);
}
cout << ans << endl;
}