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的极大值。因此,这种操作不能正确的求出区间最大值。