学习日记 2023.10.16

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值