Codeforces Round #727 (Div. 2)题解

题目链接:Codeforces Round #727 (Div. 2)

Problem.A Contest Start

思路:数学问题,看懂题目思路应该就出来了。单组复杂度\(\Theta (1)\)

\(Code:\)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define bep(i, a, b) for (int i = a; i >= b; --i)
#define lowbit(x) (x & (-x))
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;

template <typename T>
void read(T& x) {
    static char c;
    static int f;
    for (c = ch(), f = 1; c < '0' || c > '9'; c = ch())
        if (c == '-') f = -f;
    for (x = 0; c >= '0' && c <= '9'; c = ch()) x = x * 10 + (c & 15);
    x *= f;
}
template <typename T>
void write(T x) {
    static char q[65];
    int cnt = 0;
    if (x < 0) pc('-'), x = -x;
    q[++cnt] = x % 10, x /= 10;
    while (x) q[++cnt] = x % 10, x /= 10;
    while (cnt) pc(q[cnt--] + '0');
}
const int N = 1e5 + 10;
int _;
ll n,x,t;
void solve() {
    read(_);
    while (_--) {
        read(n);read(x);read(t);
        ll hh = min(t/x,n-1);
       // printf("%lld\n",hh);
        ll k = ceil(((n-1)*x - t)/(x*1.0));
       // printf("%lld\n",k);
        k = max(k,0ll);
        ll ans = k * hh + (hh + 1)*hh/2;
         write(ans);pc('\n');
    }
}
signed main() {
    solve();
    return 0;
}

Problem.B Love Song

思路:前缀和预处理一下。
\(Code:\)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define bep(i, a, b) for (int i = a; i >= b; --i)
#define lowbit(x) (x & (-x))
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;

template <typename T>
void read(T& x) {
    static char c;
    static int f;
    for (c = ch(), f = 1; c < '0' || c > '9'; c = ch())
        if (c == '-') f = -f;
    for (x = 0; c >= '0' && c <= '9'; c = ch()) x = x * 10 + (c & 15);
    x *= f;
}
template <typename T>
void write(T x) {
    static char q[65];
    int cnt = 0;
    if (x < 0) pc('-'), x = -x;
    q[++cnt] = x % 10, x /= 10;
    while (x) q[++cnt] = x % 10, x /= 10;
    while (cnt) pc(q[cnt--] + '0');
}
const int N = 3e5 + 10;
int sum[N][202];
void solve() {
    int ns, q;
    ios::sync_with_stdio(false);
    cin >> ns >> q;
    string s;cin >> s;
    s = '0' + s;
    rep(i,1,ns) {
        for (char c = 'a'; c <= 'z' ; c ++) {
            sum[i][c] = sum[i-1][c];
            if (s[i] == c)sum[i][c]++;
        }
    }
    while (q--) {
        int l, r;
        cin >> l >> r;
        ll t = 0;

        for (int i = 'a'; i <= 'z'; i ++) {
            t += (sum[r][i] - sum[l-1][i]) * (i-'a'+ 1);
        }

        cout << t << endl;
    }
}
signed main(){solve();return 0; }


Problem.C Stable Groups

思路:贪心,模拟,具体实现看代码。

\(Code:\)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define bep(i, a, b) for (int i = a; i >= b; --i)
#define lowbit(x) (x & (-x))
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;

template <typename T>
void read(T& x) {
    static char c;
    static int f;
    for (c = ch(), f = 1; c < '0' || c > '9'; c = ch())
        if (c == '-') f = -f;
    for (x = 0; c >= '0' && c <= '9'; c = ch()) x = x * 10 + (c & 15);
    x *= f;
}
template <typename T>
void write(T x) {
    static char q[65];
    int cnt = 0;
    if (x < 0) pc('-'), x = -x;
    q[++cnt] = x % 10, x /= 10;
    while (x) q[++cnt] = x % 10, x /= 10;
    while (cnt) pc(q[cnt--] + '0');
}
const int N = 200000 + 99;
ll a[N];
struct sk {
    ll fi, se;
    bool operator<(sk rhs) const{
        return rhs.fi < fi;
    }
};
priority_queue<sk>qq;
bool ff[N];
void solve() {
    ll n, k , x;
    read(n);read(k);read(x);
    ll m = k;
    for (ll i = 1; i <= n; i ++) {
        read(a[i]);
    }
    sort(a + 1, a + 1 + n);
    ll ans = 1;
    for (ll i = 1; i < n; i ++) {
        if (a[i] + x >= a[i + 1])continue;
        ans++;
        ll se = i;
        ll tr;
        ll len = a[i + 1] - a[i];
        len -= x;
        len = len / x + (len%x != 0);
        qq.push({len, se});
    }
    while (!qq.empty()) {
        auto now = qq.top();
        qq.pop();
        if (m >= now.fi) {
            ans--;
            m -= now.fi;
        }
    }
       write(ans);pc('\n');
}
signed main() {
    solve();
    return 0;
}

Problem.D PriceFixed

思路:对于每一种商品,都给了他半价时的要求,所以我们将这个要求进行排序,然后贪心,优先实现要求苛刻的,因为每一种商品都有购买数量,所以我们不可能购买多于该购买数量的。其次,我们排完序后,定义一个指针,这个指针一开始指向购买的最后一件商品,这是上限,下限是当前的半价条件和购买的件数,(我们此次计算只计算最多半价件数,最后用 \(\sum a[i]\)减去最多半价件数就是最小原价购买的数量),如果购买的件数还没达到下限(半价条件),换句话说,当前商品全部能够半价,那么上限移至当前上限-购买件数,否则我们根据下限:半价条件来计算最大半价购买数量,同时上限指针移动。

\(Code:\)

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define bep(i, a, b) for (int i = a; i >= b; --i)
#define lowbit(x) (x & (-x))
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;

template <typename T>
void read(T& x) {
    static char c;
    static int f;
    for (c = ch(), f = 1; c < '0' || c > '9'; c = ch())
        if (c == '-') f = -f;
    for (x = 0; c >= '0' && c <= '9'; c = ch()) x = x * 10 + (c & 15);
    x *= f;
}
template <typename T>
void write(T x) {
    static char q[65];
    int cnt = 0;
    if (x < 0) pc('-'), x = -x;
    q[++cnt] = x % 10, x /= 10;
    while (x) q[++cnt] = x % 10, x /= 10;
    while (cnt) pc(q[cnt--] + '0');
}
const int N = 2e5 + 10;
int _;
int n,x,t;
pair<ll,ll>a[N];
void solve() {
//    read(_);
        read(n);
        ll sum = 0;
        rep(i,1,n){
            ll u,v;
            read(u);read(v);
            a[i] = {v+1,u};
            sum += u;
        }
        sort(a+1,a+1+n);reverse(a+1,a+1+n);
        ll now = sum,ans = 0ll;
        rep(i,1,n){
        //puts("x");
            if(a[i].first > sum)continue;
            ll dis = now - a[i].first + 1;//最多个数
            ll suf = a[i].second;
            if(suf >= dis){
                now = a[i].first - 1;
                ans += dis;
            } else {
                now -= suf;
                ans += suf;
            }
        }
        ll k =( sum - ans )*2;
        ans += k;
        write(ans);pc('\n');
  //  }
}
signed main() {
    solve();
    return 0;
}


Problem.E Game with Cards

输入:第一行\(n,m\);
接下来\(n\)组每一组输入一个\(a[i]\),然后两个范围\(x[i][j],(pair,j∈[0,1])\)
思路:考虑DP,朴素做法是维护 \(f(0/1,i,j)\) 其中第一维代表的是当前更新左右手,第二维代表当前进行到第 \(i\) 个,第三维代表另一只手所在位置,其实这就是一个典型的线性DP,我们通过三维能够确定两只手的全部位置信息 ,\(f(k,i,j)\) 为真当且仅当\(f(k,i-1,j)\)为真且\(x[i][k].l \leq a[i] \leq x[i][k].r\)\(x[i][k \bigoplus 1].l \leq a[j] \leq x[i][k \bigoplus 1].r\)。时间复杂度和空间复杂度均为\(\Theta (n^2)\)。发现该\(f\)数组存储的是是否可达,因此可以利用一个\(set\)进行维护,这个操作就有点类似于离散化的操作,存储目前所有可达下标,再优化一些,我们发现我们每一次都更新该\(set\)因此我们可以反复利用一个\(set\)来减少一维(i),然后每一次更新时,我们只需要在满足\(x[i][k].l \leq a[i] \leq x[i][k].r\)时,遍历 \(set\) 集合,因为 \(set\) 集合有序,所以满足单调性,在第一个符合条件 \(x[i][k \bigoplus 1].l \leq a[j] \leq x[i][k \bigoplus 1].r\) 之后就可以退出。时间复杂度 \(\Theta (nlogn)\),然后每次记录一下路径就可以了。
\(Code:\)

/* -*- encoding: utf-8 -*-
'''
@File    :   E.cpp
@Time    :   2021/07/01 14:49:05
@Author  :   puddle_jumper
@Version :   1.0
@Contact :   1194446133@qq.com
'''

# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x){
	static char c;
	static int f;
	for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
	for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
	x*=f;
}
template<typename T>void write(T x){
	static char q[65];
	int cnt=0;
	if(x<0)pc('-'),x=-x;
	q[++cnt]=x%10,x/=10;
	while(x)
		q[++cnt]=x%10,x/=10;
	while(cnt)pc(q[cnt--]+'0');
}

const int N = 1e5+10;

int a[N];
pair<int,int>x[N][2];
int n,m;
set<pair<int,int> >f[2];
int to[N][2];
void print(int idx,int x){
	if(!idx)return ;
	print(to[idx][x],x^1);
	rep(i,to[idx][x]+1,idx)write(x),pc(' ');
}
void solve(){
	read(n);read(m);
	rep(i,1,n){
		read(a[i]);
		rep(j,0,1)read(x[i][j].first),read(x[i][j].second);
	}
	if(x[1][0].first <= a[1] and a[1]<=x[1][0].second and !x[1][1].first)f[0].insert({0,0});
	if(x[1][1].first <= a[1] and a[1]<=x[1][1].second and !x[1][0].first)f[1].insert({0,0});
	rep(i,2,n){
        bool x1 = f[1].empty(),x0 = f[0].empty();
		if(!x1){f[0].insert({a[i-1],i-1}); }
		if(!x0){f[1].insert({a[i-1],i-1}); }
		rep(j,0,1){
			if(x[i][j].first<=a[i] and a[i]<=x[i][j].second){
				while(f[j].size() and (*f[j].begin()).first<x[i][j^1].first){f[j].erase(f[j].begin()); }
				while(f[j].size() and (*f[j].rbegin()).first>x[i][j^1].second){f[j].erase(*f[j].rbegin()); }

			}else f[j].clear();
			if(!f[j].empty())to[i][j] = (*f[j].begin()).second;
		}
	}
	if(!f[0].empty())puts("Yes"),print(n,0);
	else if(!f[1].empty())puts("Yes"),print(n,1);
	else puts("No");
}


signed main(){solve();return 0; }


Problem.F Strange Array

这个题意有点难理解;
思路:该题需要求中位数,因此我们想到对于每一次询问,首先考虑该值比中值要小,我们将小于等于该数的设为-1,相应的将大于该数的设为1,于是这样就将题意转化为求\(1 \leq l \leq i \leq r \leq n\)的范围里,最大的和,因为该题要求的距离=(大于a[i]的个数-小于等于a[i]的个数)/2 ,所以可以这样转化,通过转化我们发现问题好像简化了一点点,但是如果每一次都进行赋值1,-1的话,复杂度过不去,问题就是每一次进行赋值的操作,对于求区间最大值,我们可以通过维护一个线段树来进行求解,具体做法是:首先进行维护数组前缀和,然后对于前缀和建立线段树,维护最大值,最小值,然后将区间最大值转化为询问 \([i,n]\) 中的最大值-询问 \(1~i-1\) 中的最小值。解决了区间最大值问题之后,我们考虑优化赋值问题,我们可以通过对于数组进行排序操作来解决,遍历排序之后的数组,因为排序之后每一次移动,只需要更改当前指向的值为-1,其它值并不用动,因为我们维护的是前缀和,所以该操作对应的应为线段树区间修改,利用懒惰节点可以\(\Theta (logn)\)的实现修改操作。到这里,问题就解决了,该值比中值大的情况反过来就可以。然后需要再注意一些细节。
\(Code:\)


/* -*- encoding: utf-8 -*-
'''
@File    :   F.cpp
@Time    :   2021/07/02 08:32:34
@Author  :   puddle_jumper 
@Version :   1.0
@Contact :   1194446133@qq.com
'''

# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x){
	static char c;
	static int f;
	for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
	for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
	x*=f;
}
template<typename T>void write(T x){
	static char q[65];
	int cnt=0;
	if(x<0)pc('-'),x=-x;
	q[++cnt]=x%10,x/=10;
	while(x)
		q[++cnt]=x%10,x/=10;
	while(cnt)pc(q[cnt--]+'0');
}


const int N = 2e5+10;
int mi;

struct T{
	int l,r,Max,Min,tag;
}tr[N<<2];

//注意我们是在原未排序数组中建树维护前缀和

void buildL(int l,int r,int p){
	int mid = l + r >> 1;
	if(l == r){
		tr[p] = {l,r,l,l,0};return ;
	}
	buildL(l,mid,p<<1);buildL(mid+1,r,p<<1|1);
	tr[p] = {l,r,max(tr[p<<1].Max , tr[p<<1|1].Max),min(tr[p<<1].Min , tr[p<<1|1].Min),0};
}





void down(int p){
	if(!tr[p].tag)return ;
	tr[p<<1].Max += tr[p].tag,tr[p<<1|1].Max += tr[p].tag;
	tr[p<<1].Min += tr[p].tag,tr[p<<1|1].Min += tr[p].tag;
	tr[p<<1].tag += tr[p].tag,tr[p<<1|1].tag += tr[p].tag;
	tr[p].tag = 0;return;
}



int queryMax(int s,int t,int p){
	if(tr[p].l > t or tr[p].r < s)return -1e8;
	if(s <= tr[p].l and tr[p].r<= t){return tr[p].Max; }
	down(p);
	return  max(queryMax(s,t,p<<1),queryMax(s,t,p<<1|1));
}
int queryMin(int s,int t,int p){
	if(tr[p].l > t or tr[p].r < s)return 1e8;
	if(s <= tr[p].l and tr[p].r<= t){return tr[p].Min; }
	down(p);
	return  min(queryMin(s,t,p<<1),queryMin(s,t,p<<1|1));
}
void modify(int s,int t,int p,int k){
	if(tr[p].l > t or tr[p].r < s)return ;
	if(s <= tr[p].l and tr[p].r <= t){tr[p].Max+=k;tr[p].Min+=k;tr[p].tag += k;return ; }
	down(p);
	modify(s,t,p<<1,k);modify(s,t,p<<1|1,k);
	tr[p].Max = max(tr[p<<1].Max,tr[p<<1|1].Max);
	tr[p].Min = min(tr[p<<1].Min,tr[p<<1|1].Min);
}

struct nodex{
	int a,id;
	bool operator<(const nodex &x){
		return x.a>a;
	}
}a[N];

int n;
int ans[N];
void work(int idx,int idy){
	if(idx>idy)swap(idx,idy);
	rep(i,idx,idy){
		modify(a[i].id,n,1,-2);
	}
}
void print(){rep(i,1,n)printf("%d ",ans[i]); }
int nxt[N];
void init(){
	bep(i,n,1){
		int idx = i;
		while(a[i].a == a[i-1].a)i--;
		nxt[idx] = i;
	}
}
void solve(){
	read(n);
	mi = n+1;
	rep(i,1,n)read(a[i].a),a[i].id = i,mi = min(a[i].a,mi);
	buildL(1,n,1);
	sort(a+1,a+1+n);
	init();
	rep(i,1,n){
		int idx = i;
		while(i+1 <= n and a[i].a == a[i+1].a){
			auto a1 = queryMin(1,a[i].id-1,1);
			auto a2 = queryMax(a[i].id,n,1);
			a1 = min(0,a1);
			if(a[i].id == 1)a1 = 0;
			ans[a[i].id] = (a2 - a1)/2;i++;
		}
		auto a1 = queryMin(1,a[i].id-1,1),a2 = queryMax(a[i].id,n,1);
		a1 = min(0,a1);
		if(a[i].id == 1)a1 = 0;
		ans[a[i].id] = (a2-a1)/2;
		work(idx,i);
	}
	// rep(i,1,n){
	// 	printf("%d ",ans[i]);
	// }
	//memset(tr,0,sizeof tr);
	buildL(1,n,1);
	bep(i,n,1){
		int idx = i;
		int idy = nxt[idx];
		idy = max(1,idy);
		
		bep(j,idx,idy){
			auto a1 = queryMax(a[j].id,n,1);
			auto a2 = queryMin(1,a[j].id-1,1);
			a2 = min(0,a2);
			if(a[j].id == 1)ans[1] = max(ans[1],(a1+1)/2-1);
			else 
			ans[a[j].id] = max(ans[a[j].id],(a1-a2+1)/2-1);
		}
		work(idx,idy);
		i = idy;
	}
	print();
}


signed main(){solve();return 0; }

写在最后

F题我一开始想的区间最大值操作并不是维护前缀和,这样明显是不对的,因为有负值的存在,即该区间可能并不是真正最大值,可能他的子区间的子区间存在一个小于0的极小值,一个大于0的极大值。因此,这种操作不能正确的求出区间最大值。

posted @ 2021-07-02 17:08  xiaodangao  阅读(146)  评论(0编辑  收藏  举报