Codeforces Round #672 (Div. 2) C2. Pokémon Army (hard version) D. Rescue Nibel!

Codeforces Round #672 (Div. 2)

C2. Pokémon Army (hard version)

题意:给你一个a数组让你找到一个子数组b求 b[1] - b[2] + b[3] - b[4]……的最大值并且有q次询问每次询问一个l和r表示将a数组的a[l],与a[r]交换, 每次询问都让你求一次最大值。

题解:

如果你c1是用dp写的话, 这题就不好写了。

可以想一下每次从a选一个子数组要求答案最大的话, 其值就是查分数组的大于的0和。

如果知道了这个那么每次询问只要维护 4个值就行了。在更新答案。

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

ll n, a[N], x[N];

void solve() {
    int q;
    cin >> n >> q;
    for (int i = 0; i <= n + 1; i++) {
        x[i] = 0;
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i];

    }
    a[n + 1] = 0;
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        x[i] = a[i] - a[i + 1];
        if (x[i] > 0) ans += x[i];
    }

    cout << ans << endl;

    while (q--) {
        int l, r; cin >> l >> r;
        swap(a[l], a[r]);
        if (l == r) {
            cout << ans << endl;
            continue;
        }
        if (x[l] > 0) ans -= x[l];
        if (x[r] > 0) ans -= x[r];
        if (x[l - 1] > 0) ans -= x[l - 1];
        if (x[r - 1] > 0 && r - 1 != l) ans -= x[r - 1];

      
        x[l - 1] = a[l - 1] - a[l];
        x[l] = a[l] - a[l + 1];
        x[r] = a[r] - a[r + 1];
        x[r - 1] = a[r - 1] - a[r];
        if (x[l] > 0) {
            ans += x[l];
        } 
        if (x[r] > 0) {
            ans += x[r];
        }
        if (l - 1 && x[l - 1] > 0) {
            ans += x[l - 1];
        }
        if (r - 1 && l != r - 1&& x[r - 1] > 0) {
            ans += x[r - 1];
        }
        cout << ans << endl;  
    }

}



int main() {
    ios::sync_with_stdio(0);
    int t; cin >> t;
    while (t--) {
        solve();
    }
}

D. Rescue Nibel!

题意:

给你n盏灯, 每盏灯都会在固定时间打开, 问选k盏灯且这k盏灯在某一个时刻一定全部打开,有多少种选择方法?

题解:

这题比赛的时候没有想出来。。。太菜了。

如果枚举每个时刻有多少盏灯打开, 那在这个时刻的答案是 $$\binom{k}{同时打开的灯} $$

但是会有重复计算。

所以我们可以每次只计算与开始打开灯贡献其它就不用在算了。

\(x[i]\)表示第 \(i\)个时刻有 \(x[i]\) 个灯打开。

\(vis[i]\) 表示 第\(i\)个时刻刚打开 \(vis[i]\)个灯。

那么第\(i\) 个时刻的答案就是 \(\binom{k}{x[i]} - \binom{k}{x[i] - vis[i]}\)

这样计算就会有重复的啦。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 7e5 + 7;
const ll mod = 998244353;
long long fac[N]; // 阶乘表
ll n, k;

vector<ll> g;

vector<pair<ll, ll> > cnt;

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

long long qpow(long long x, long long n) { 
	long long res = 1; 
	for (; n; n >>= 1, x = x * x % mod) 
		if (n & 1) res = res * x % mod; 
	return res; 
}

long long inv(long long a) { // 返回逆元 费马小定理
	return qpow(a, mod-2)%mod;
}

void solves() { // 计算阶乘表
	fac[0] = 1;
	for(int i = 1;i < N; i++) {
		fac[i] = (fac[i-1]*i)%mod;
	}
}

long long comb(long long n, long long k) {
	if(k > n) return 0;
	return (fac[n]*inv(fac[k])%mod * inv(fac[n-k])%mod) % mod;
}

ll x[N], vis[N];

void solve() {
    solves();
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        ll l, r; cin >> l >> r;
        g.push_back(l), g.push_back(r);
        cnt.push_back({l, r});
    }
    sort(g.begin(), g.end());
    g.erase(unique(g.begin(), g.end()), g.end());

    for (int i = 0; i < cnt.size(); i++) {
        int l = get_id(cnt[i].first), r = get_id(cnt[i].second);
        x[l]++, x[r + 1]--;
        vis[l]++;
    }
    for (int i = 1; i <= g.size() + 10; i++) {
        x[i] += x[i - 1];
    }
    ll ans = 0;
    for (int i = 1; i <= g.size() + 10; i++) {
        if (vis[i]) {
       
            ans = (ans + comb(x[i], k)) % mod;
            ans = (ans - comb((x[i] - vis[i]), k) + mod) % mod;
        }
            
    }
    cout << ans << endl;



}



int main() {
    ios::sync_with_stdio(0);
    int t = 1; //cin >> t;
    while (t--) {
        solve();
    }
}
posted @ 2020-09-26 00:02  ccsu_zhaobo  阅读(153)  评论(0编辑  收藏  举报