Codeforces Round 917 (Div. 2)(A~D)(又是数学题)

A - Least Product 

        题意:Codeforces Round 917 (Div. 2)(A~D)(又是数学题)_第1张图片

思路:若有奇数个负数,则不需要任何操作。若存在0,也不需要任何操作。其余情况将任意一个数改为0即可。

        

#include 
using namespace std;
void solve() 
{
	int n;
	cin >> n;
	int a[n + 5];
	for(int i = 0 ; i < n ; i ++)
		cin >>a[i];
	int cnt = 0;
	for(int i = 0 ; i < n ; i ++){
		if(a[i] < 0){
			cnt++;
		}
		if(a[i] == 0){
			cout << 0 <>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

B - Erase First or Second Letter 

        题意:Codeforces Round 917 (Div. 2)(A~D)(又是数学题)_第2张图片

        思路:由于只能删除前两个数,因此可以考虑固定字符串开头,然后每次删除第二个数就会多形成一个空字符串。然后遍历每一个字符,这样做是O(N^{2})的。可以发现:对于同一个开头字母而言,前面位置删除后所能形成的字符串必然涵盖了后面位置开头所形成的字符串,因此不需要遍历所有字符,只需要遍历所有字母即可。这样复杂度是 O(26 * N)的。

#include 
using namespace std;
void solve() 
{
	int n;
	string s;
	cin >> n;
	int inf = n + 1;
	cin >> s;
	vectorP(26 , inf);
	for(int i = 1 ; i <= n ; i ++){
		P[s[i - 1] - 'a'] = min(P[s[i - 1] - 'a'] , i);
	}	
	int ans = 0;
	for(int i = 0 ; i < 26 ; i ++){
		ans += inf - P[i];
	}
	cout << ans <>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

C - Watering an Array 

        题意:Codeforces Round 917 (Div. 2)(A~D)(又是数学题)_第3张图片

               思路:假设一开始数组全为0,要让a_{j} = j的最少操作需要j次,且有\forall i(a_{i} = j > i)(i < j)\forall i(a_{i} <= j < i)(i > j),因此整个数组的价值最多为1。由此可以发现,对于全是0的数组而言,只需要前一天选择操作1,让a_{1} = 1 , 第二天选择操作2。这样就能得1分,且这么做一定是最优的。由于一开始不一定全是0,所以在第一次使用操作2了之后,每两天能够得一分。接下来只需要枚举什么时候第一次用操作2就行了。

        假设第 i 天第一次使用操作2,那么总共的得分为 初始数组得分 + (d - i) /2。而什么情况下会使得总得分更高,即连续使用操作1之后,初始数组得分变的更高了。由于初始数组得分最高为n,所以只需要遍历前2 * n天使用操作1即可。复杂度为O(2 * N^{2})

        

#include 
using namespace std;
const int inf = 100;
void solve() 
{
	int n , k , d;
	cin >> n >> k >> d;
	int arr[n + 5] , brr[k + 5];
	for(int i = 1 ; i <= n ; i ++){
		cin >> arr[i];
	}
	for(int i = 0 ; i < k ; i ++){
		cin >> brr[i];
	}
	int ans = -inf;
	int dd = 0;
	for(int i = 0 ; i < min(d , 2 * n) ; i ++){
		int num = 0;
		for(int j = 1 ; j <= n ; j ++){
			num += (arr[j] == j);
		}
		ans = max(ans , num + (d - 1 - i) / 2);
		for(int j = 1 ; j <= brr[dd] ; j ++){
			arr[j]++;
		}
		dd++;
		dd %= k;
	}
	cout << ans << endl;
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

D - Yet Another Inversions Problem

Codeforces Round 917 (Div. 2)(A~D)(又是数学题)_第4张图片

思路: 对于一个数内部,即a_{ik}a_{ik + k - 1}之间的逆序对而言,只需要对q数组求一次逆序对即可。

  接下来考虑数与数之间的情况。对于a_{i}而言,大小在(a_{i} , 2 * a_{i})之间的数而言,a_{i}*2^{p_{j}}与这些数能够形成的逆序对为(a_{i}*2^{p_{j}} , a_{x} * 2^{p_{j} - 1})(a_{i}*2^{p_{j}} , a_{x} * 2^{p_{j} - 2})....(a_{i}*2^{p_{j}} , a_{x} * 2^{0})总共有p_{j}个。因此a_{i}与这些数所能形成的逆序对数量为\sum_{i = 0}^{k} p_{i} = (0 + k - 1) * k / 2。而对于大小在(2 * a_{i} , 4 * a_{i})的数而言,a_{i}*2^{p_{j}}与这些数能够形成的逆序对为(a_{i}*2^{p_{j}} , a_{x} * 2^{p_{j} - 2})(a_{i}*2^{p_{j}} , a_{x} * 2^{p_{j} - 3})....(a_{i}*2^{p_{j}} , a_{x} * 2^{0})总共有p_{j} - 1。因此因此a_{i}与这些数所能形成的逆序对数量为\sum_{i = 0}^{k} p_{i} = (0 + k - 2) * (k - 1) / 2,如此不断往上倍增求答案,直到上限为止。

        而相反,大小在((a_{i}+ 1)/ 2, a_{i})之间的数而言,也是同样的考虑方式,如此不断除以二直到下限为止。详细可以看注释

        

#include 
using namespace std;
#define LL long long
#define int long long
const LL mod = 998244353;
const int N = 5e5;
struct BIT{//Binary indexed Tree(树状数组)
	int n;
	vector tr;
	BIT(int n) : n(n) , tr(n + 1 , 0){
	}
	int lowbit(int x){
		return x & -x;
	}
	void modify(int x , int modify_number){
		for(int i = x ; i <= n ; i += lowbit(i)){
			tr[i] += modify_number;
		}
	}
	void modify(int l , int r , int modify_number){
		modify(l , modify_number);
		modify(r + 1 , -modify_number);
	}
	int query(int x){
		int res = 0;
		for(int i = x ; i ;  i -= lowbit(i))
			res += tr[i];
		return res;
	}
	int query(int x , int y){
		return query(y) - query(x);
	}
};
int tmp[N];
LL merge_sort(int q[], int l, int r)
{
    if (l >= r) return 0;
    
    int mid = (l + r) >> 1; // 二分区间
    
    LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);
    //归并
    int i = l, j = mid + 1, k = 0;
    
    while (i <= mid && j <= r)
    {
        if (q[i] <= q[j]) tmp[k ++] = q[i ++]; // 前面的排序正常,注意`=` 说明不是逆序对
        else
        {
            res += mid - i + 1;
            tmp[k ++] = q[j ++];
        }
    }
    
    // 扫尾工作
    while (i <= mid) tmp[k ++] = q[i ++];
    while (j <= r) tmp[k ++] = q[j ++];
    
    for (int i = l, j = 0; i <= r; i ++ , j ++) q[i] = tmp[j];
    
    return res;
}
void solve() 
{
	int n , k;
	cin >> n >> k;
	BIT num(4 * n + 5) , sum(k + 5);
	int a[n] , b[k];
	for(int i = 0 ; i < n ; i ++){
		cin >> a[i];
		num.modify(a[i] , 1);
	}
	for(int i = 0 ; i < k ; i ++){
		cin >> b[i];
	}
	int ans = merge_sort(b , 0 , k - 1);//自己内部的贡献
	ans *= n;//
	ans %= mod;
	for(int i = 0 ; i < n ; i ++){
		int x = a[i];//x 与 后面所形成的的逆序对
		int cnt = 1;
		while(x < 2 * n ){
			int t = x * 2;
			int nn = num.query(x , t);//a[i] * 2 ^ cnt > 这些数
			if(nn == 0){
				x = t;
				cnt++;
				continue;
			}
			int end = max(1LL * 0 , k - cnt);
			int ns = (1 + end) * end / 2;
		//	cout << x << " " << t << " " << nn << endl;
	//		cout << "cnt :" << cnt <<"ns :" << ns << endl;
			ans += ns * nn;
			ans %= mod;
			x = t;
			cnt++;
		}
		x = a[i];
		cnt = 1;
		while(1){
			if(x == 1)
				break;
			int t = (x + 1) / 2;
			int nn = num.query(t - 1  , x - 1);//这些数 * 2 ^ cnt > a[i]
/*			cout << t - 1 << " " << x - 1 << " " << nn << endl;*/
			if(nn == 0){
				x = t;
				cnt++;
				continue;
			}
			int st = min(k , cnt );//
			int cntt = k - st + 1;
			int res = k - cntt;
			int ns =  k * res + (st + k) * cntt / 2;
/*			cout << "cnt :" << cnt <<"ns :" << ns << endl;*/
			ans += nn * ns;
			ans %= mod;;
			x = t;
			cnt++;
		}
		num.modify(a[i] , -1);
	}
	cout << ans << endl;
}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

你可能感兴趣的:(Codeforces,算法,c++,数据结构)