Codeforces Round #672 (Div. 2)

C1 & C2

题意

给定一个长度为n的序列,从中按顺序选择一些下标使得加上奇数位置减去位置的值最大,最后修改某两个位置,并求出它的答案

题解

我们可以发现这个一定是先选∧ 再选 V 的顶点,因为选其他对这个数列只能有减少不能增大,并且第一个和最后一个一定是∧类型的所以基于这种性质我们让边界值为最小,我们就能间接的控制第一和最后一个的类型,中间的必然是一个∧一个V所以我们只需要加上∧减去V为顶点的值就可以得到答案,在后面的q次操作中同理也就最多可能改变6个点的状态,所以只需要对这六个点判断就能得到答案。

代码

#include<iostream>
#include<cstring>
#include<cmath>

using namespace std;
const int N = 3e5+10;
int a[N];
long long ans = 0;
// 先去除原来点的状态 
void erase(int u){
    if(a[u] > a[u - 1] && a[u] > a[u + 1]) ans -= a[u];
    if(a[u] < a[u - 1] && a[u] < a[u + 1]) ans += a[u];
}
// 加上现在点的状态 
void insert(int u){
    if(a[u] > a[u - 1] && a[u] > a[u + 1]) ans += a[u];
    if(a[u] < a[u - 1] && a[u] < a[u + 1]) ans -= a[u];
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
	    ans = 0;
		int n,k;
		scanf("%d%d",&n,&k);
		for(int i = 1;i<=n;i++)
			scanf("%d",&a[i]);
		a[0] = a[n + 1] = -1;  // 使第一个和最后一个必然是^
		for(int i = 1;i<=n;i++){
			if(a[i] > a[i - 1]&& a[i] > a[i + 1]) ans += a[i]; // 加^ 
			if(a[i] < a[i - 1]&& a[i] < a[i + 1]) ans -= a[i]; // 减V 
		}
		cout<<ans<<endl;
		while(k--){
			int l,r;
			scanf("%d%d",&l,&r);
			if(l - 1 > 0)   // 判断 六个点状态  + 判重 
				erase(l - 1);
			erase(l);
			erase(l + 1);
			if(l != r){
				if(l + 1 != r&& l - 1 != r&&l != r)
					erase(r);
				if(l + 1 != r - 1&& l - 1 != r - 1&&l != r - 1)
					erase(r - 1);
				if(l + 1 != r + 1&& l - 1 != r + 1&&l != r + 1)
					erase(r + 1); 
			}
			swap(a[l],a[r]);
			if(l - 1 > 0) 
				insert(l - 1);
			insert(l);
			insert(l + 1);
			if(l != r){
				if(l + 1 != r&& l - 1 != r&&l != r)
					insert(r);
				if(l + 1 != r - 1&& l - 1 != r - 1&&l != r - 1)
					insert(r - 1);
				if(l + 1 != r + 1&& l - 1 != r + 1&&l != r + 1)
					insert(r + 1);
			}
			cout<<ans<<endl;
		}
	}	
	
	return 0;
} 

D

题意

给定n个区间,需要在一个点选择k个区间查询所以可能情况

题解

在区间问题上大多数都是只用考虑区间的端点,而且区间有一个性质就是统计在某个点区间的个数的时候可以给区间的左端点 + 1 (右端点 + 1) 的那个端点 - 1 从左往右遍历就能算出在改点的区间个数,所以基于这个性质我们只需要记录在区间的左端点和(右端点 + 1)的点的状态所以答案就是 对于每个点选择k个 - 从这个点上个点遗留的点中选择k个 的方案数之和我使用两个map记录一个在改点的 + 和 - 对于每个点就能很好的记录改点的上一个点遗
留的点,即上个点所有点 - -map的点 改点的所有点就是 在- -map基础上 + +map里面的最后处理选择情况统计答案

代码

#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
#define x first
#define y second
typedef pair<int ,int > PII;
const int N = 3e5+10,mod = 998244353;
PII a[N];
int fact[N];
int infact[N];
vector<int > nums;

map<int ,int >mp1;
map<int ,int > mp2;
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b&1) res = 1ll * res * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1; 
	}
	return res;
}
void init(int n){  
	fact[0] = infact[0] = 1;
	for(int i = 1;i<=n;i++)
	{
		fact[i] = 1ll * fact[i - 1] * i % mod;
		infact[i] = 1ll * infact[i - 1] * qmi(i,mod - 2) % mod; 
	}
}
int C(int a,int b){
	if(a < b) return 0;
	return 1ll * fact[a] * infact[b] % mod * infact[a - b];
}
int main(){
    init(N - 1);
	int n,k;
	scanf("%d%d",&n,&k);
	for(int i = 1;i<=n;i++){
		int l,r;
		scanf("%d%d",&l,&r);
		mp1[l] ++;
		mp2[r + 1] --;
		nums.push_back(l);
		nums.push_back(r + 1);
	}
	sort(nums.begin(),nums.end());
	nums.erase(unique(nums.begin(),nums.end()),nums.end());
	int ans = 0;
	int cnt = 0;
// 	cout<<C(4,3)<<endl;
	for(auto x:nums){
	   // cout<<x<<endl;
	    cnt += mp2[x];
	    int t = cnt;
	    cnt += mp1[x];
	    ans = (ans + 0ll - C(t,k) + C(cnt,k))%mod;
	   // cout<<cnt << ' '<<t<<endl;
	   // cout<<C(cnt,k)<<' '<<C(t,k)<<' '<<x<<' '<<ans<<endl;

	}
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值