Codeforces Round #695

4 篇文章 0 订阅
3 篇文章 0 订阅

Codeforces Round #695 (Div. 2)

B. Hills And Valley

题解:模拟改变每个数, 对于 a [ i ] a[i] a[i]计算 变成 a [ i − 1 ] a[i - 1] a[i1]的贡献, 和 a [ i + 1 ] a[i + 1] a[i+1]的贡献,枚举每个数, 最后取最优的就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 7;

int a[N], n;

int cal(int pos) {
    if (a[pos] > a[pos - 1] && a[pos] > a[pos + 1]) {
        return 1;
    } else if (a[pos] < a[pos - 1] && a[pos] < a[pos + 1]) {
        return 1;
    }
    return 0;
}

int work(int pos) {
    int ans = 0;
    if (pos > 2 && pos < n - 1) {
       ans += cal(pos);
       ans += cal(pos - 1);
       ans += cal(pos + 1);
        
    } else if (pos < n - 1) {
        ans += cal(pos);
        ans += cal(pos + 1);
    } else if (pos > 2){
        ans += cal(pos);
        ans += cal(pos - 1);
    } else if (pos == 2 && pos == n - 1) {
        ans += cal(pos);
    }
    return ans;
}

void solve(int j) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    if (n < 3) {
        cout << 0 << endl;
    } else {
        int ans = 0;
        for (int i = 2; i < n; i++) {
            ans += cal(i);
        }
        int res = 0;
        for (int i = 2; i < n; i++) {
            int temp = a[i];
            int cnt = work(i);
            a[i] = a[i - 1];
            int cnt1 = work(i);
            a[i] = a[i + 1];
            int cnt2 = work(i);
            a[i] = temp;
            res = max(res, cnt - min(cnt1, cnt2));
        }
        cout <<  ans - res << endl;
    }


}

int main() {
    int t; cin >> t;
    for (int j = 1; j <= t; j++) {
        solve(j);
    }
}

C. Three Bags

题解:总共有 3 ∗ n 3 * n 3n 的物品, 也就是说要操作 3 ∗ n − 1 3 * n - 1 3n1 次才结束, 把每次操作看成一条边, 也就是 3 ∗ n − 1 3 * n - 1 3n1次操作刚好构成一颗节点为 3 ∗ n 3 * n 3n的树。

例如:

在这里插入图片描述

这个树表示的操作是 (4, 3), (3, 2), (2, 1)然后答案就是 a − b + c − d a - b + c - d ab+cd,也就是深度为奇数的要减(根节点的深度为0), 要向总共的和最大,就要减最少。

因为,相同背包的物品不能进行操作,所以要满足下面条件的任意一个:

  1. 如果奇数层为同一种背包的物品,那么这种背包的所有物品都必须在奇数层。这个很好证明, 如果奇数只会一种背包的物品,且还有这种背包的物品在偶数层,那就没法合并了。

  2. 奇数层至少含有两种背包。

有了这个答案就很好构造了,要么取每一种背包和最小的, 要么从三种背包种取两个不同种背包最小的。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 7;
typedef long long ll;
ll a[4][N], n1, n2, n3, t;

void solve() {
    ll sum1 = 0, sum2 = 0, sum3 = 0, ans;
    cin >> n1 >> n2 >> n3;
    for (int i = 1; i <= n1; i++) {
        cin >> a[1][i];
        sum1 += a[1][i];
    }
    sort(a[1] + 1, a[1] + n1 + 1);
    for (int i = 1; i <= n2; i++) {
        cin >> a[2][i];
        sum2 += a[2][i];
    }
    sort(a[2] + 1, a[2] + n2 + 1);
    for (int i = 1; i <= n3; i++) {
        cin >> a[3][i];
        sum3 += a[3][i];
    }
    sort(a[3] + 1, a[3] + n3 + 1);

    ans = sum1 + sum2 + sum3;
    ll cnt = a[1][1] + a[2][1] + a[3][1];
    cnt = cnt - max(max(a[1][1], a[2][1]), a[3][1]);
    ans = ans - 2 * min(min({sum1, sum2, sum3}), cnt);
    cout << ans << endl;


}

int main() {
    t = 1;
    while (t--) {
        solve();
    }
}

D. Sum of Paths

题解:

d p [ i ] [ j ] [ 0 ] dp[i][j][0] dp[i][j][0]表示到达 i i i点还剩 j j j步所走的贡献是多少, d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]表示,从其它点 到达 i i i点还剩 j j j步,有多少条路径。

s u m [ i ] sum[i] sum[i]表示 第 i i i个节点走了多少次。

代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 5e3 + 7;
typedef long long ll;
const ll mod = 1e9 + 7;
ll n, k, q, a[N], dp[N][N][2];
ll sum[N];

int main() {
    cin >> n >> k >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        dp[i][0][0] = a[i];
        dp[i][0][1] = 1;
    }

    for (int i = 1; i <= k; i++) {
        for (int j = 1; j <= n; j++) {
            if (j == 1) {
                dp[j][i][0] = dp[j + 1][i - 1][0] + dp[j + 1][i - 1][1] * a[j]; 
                dp[j][i][1] = dp[j + 1][i - 1][1];
            } else if (j == n) {
                dp[j][i][0] = dp[j - 1][i - 1][0] + dp[j - 1][i - 1][1] * a[j]; 
                dp[j][i][1] = dp[j - 1][i - 1][1];
            } else  {
                dp[j][i][0] = dp[j - 1][i - 1][0] + dp[j - 1][i - 1][1] * a[j]; 
                dp[j][i][1] = dp[j - 1][i - 1][1];
                dp[j][i][0] += dp[j + 1][i - 1][0] + dp[j + 1][i - 1][1] * a[j]; 
                dp[j][i][1] += dp[j + 1][i - 1][1];
            }
            dp[j][i][0] %= mod;
            dp[j][i][1] %= mod;
        }
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += dp[i][k][0];
        ans %= mod;
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= k; j++) {
            sum[i] += dp[i][j][1] * dp[i][k - j][1];
            sum[i] %= mod;
        }
    }

    while (q--) {
        ll x, y; scanf("%lld %lld", &x, &y);
        if (y >= a[x]) {
            ll cnt = y - a[x];
            ll cat = (cnt % mod * sum[x] % mod) % mod;
            ans = ans + cat;
            ans = ans % mod;
        } else {
            ll cnt = a[x] - y;
            ll cat = (cnt % mod * sum[x] % mod) % mod;
            ans = (ans - cat + mod) % mod;
        }
        a[x] = y;
        printf("%lld\n", ans);
    }

}
 

E. Distinctive Roots in a Tree

题解:

这题就比较难想。

我可以先求出非法的节点, 然后剩下来的就是合法的点。

哪些点是非法的呢?

  1. 如果节点 u u u 的权值,除了节点 u u u孩子节点权值,其它点有与 u u u权值相等的点,那么可以证明节点 u u u和它的孩子节点全部不合法。(这里的孩子节点包含儿子节点,孙子节点, 孙子的孙子……)
  2. 如果有一个儿子节点 v v v, 其父亲节点为 u u u是,如果 v v v的孩子节点中有一个权值和父亲节点的权值相等, 那么可以证明, 除了 u u u的孩子节点,其它节点都不合法。

至于为啥?仔细想一下就明白了。

有了这个我没就可以把不合法的给去掉,剩下来的一定是合法的。

代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 7;

int a[N], n, all[N];

vector<int> g[N], v;

map<int, int> mp[N];

int get_id(int x) {
	return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}

int id[N], sum[N], vis[N], sz[N], top = 1;

int cat[N];

int merge(int x, int y) {
	if (mp[x].size() > mp[y].size()) {
		for (auto it: mp[y]) {
			mp[x][it.first] += it.second;
		}
		mp[y].clear();
		return x;
	} else {
		for (auto it: mp[x]) {
			mp[y][it.first] += it.second;
		}
		mp[x].clear();
		return y;
	}

}

void dfs(int u, int fa) {
	id[u] = top++;
	sz[u] = 1;
	mp[cat[u]][a[u]] = 1;
	for (int to: g[u]) {
		if (to == fa) continue;
		dfs(to, u);
		
		sz[u] += sz[to];
		if (mp[cat[to]][a[u]]) {
			sum[1]++;
			sum[id[to]]--;
			sum[id[to] + sz[to]]++;
		}
		cat[u] = merge(cat[u], cat[to]);
	}
	if (all[a[u]] > mp[cat[u]][a[u]]) {
		sum[id[u]]++;
		sum[id[u] + sz[u]]--;
	}

}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		v.push_back(a[i]);		
		cat[i] = i;
	}
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(), v.end()), v.end());
	for (int i = 1; i <= n; i++) {
		a[i] = get_id(a[i]);
		all[a[i]]++;
	}


	for (int i = 1; i < n; i++) {
		int u, v; cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		sum[i] = sum[i - 1] + sum[i];

		if (sum[i] == 0) {
			ans++;
		}
	}
	
	cout << ans << endl;


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值