Editorial of Codeforces Round #672 题解

Problem A

题目描述
将给出长为n的数组排序为不减序列,每次只能交换相邻位置的数,问能否在 n ( n − 1 ) 2 − 1 \frac{n(n-1)}2-1 2n(n1)1次以内完成。
题目解析
对数组排序的过程实际上就是一个冒泡排序的过程。冒泡排序的最坏复杂度是 O ( n 2 ) O(n^2) O(n2),此时的序列是降序的,执行交换的次数恰好为 n ( n − 1 ) 2 \frac{n(n-1)}2 2n(n1)。所以这个题就转变为了求:数组是否为降序序列。
但是这里还有一个问题没有解决:如果数组中有重复元素应当如何处理?
以序列 { 5 , 4 , 4 , 2 , 1 } \{5,4,4,2,1\} {5,4,4,2,1}为例,该序列等同于 { 5 , 3 , 4 , 2 , 1 } \{5,3,4,2,1\} {5,3,4,2,1},不满足上述条件,所以输出YES

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

const int maxn = 5e4+5;

int t,len;
int store[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cin>>t;
    while(t--){
        cin>>len;
        rep(i,0,len)
            cin>>store[i];
        bool flag = true;
        rep(i,1,len){
            flag = flag & (store[i-1]>store[i]);
        }
        if(flag){
            cout<<"NO"<<endl;
        }else{
            cout<<"YES"<<endl;
        }
    }
    re 0;
}

Problem B
题目描述
给出一个序列 { a n } \{a_n\} {an},找出序列中有序对 ( i , j ) (i,j) (i,j)的个数。其中要求 i < j i<j i<j,且 a i & a j ≥ a i ⊕ a j a_i \& a_j \geq a_i \oplus a_j ai&ajaiaj
题目解析
在开始之前先定义一个名词:记二进制表示下,从最高位起,第一位不为零的位置到最低位的长度为位长度。如3的二进制为11,位长度为2;4的二进制为100,位长度为3。
对于AND来说,同为真;对于XOR来说,异为真。由此我们可知,满足上述条件的 a i , a j a_i,a_j ai,aj必须位长度相同,当且仅当 a i = = a j a_i==a_j ai==aj时取等号,否则一定是 a i & a j < a i ⊕ a j a_i\&a_j < a_i \oplus a_j ai&aj<aiaj

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn = 1e5+5;

int t,n;
ll sumNum[maxn][32];
int start[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin>>t;
    while(t--){
        mem(sumNum,0);
        cin>>n;
        rep(i,0,n){
            int tmp;
            cin>>tmp;
            int pos = -1;
            while(tmp){
                tmp = tmp>>1;
                pos ++;
            }
            start[i] = pos;
            sumNum[i][pos] ++;
        }
        ll ans = 0;
        rep(i,1,n)
            rep(j,0,31){
                sumNum[i][j] += sumNum[i-1][j];
                if(j==start[i])
                    ans += sumNum[i][j] - 1;
            }

        cout<<ans<<endl;
    }

    re 0;
}

Problem C1
题目描述
从数组 { a n } \{a_n\} {an}中挑一个子序列 { b 1 , b 2 , ⋯   , b k } \{b_1,b_2,\cdots,b_k\} {b1,b2,,bk},其中 1 ≤ b 1 < b 2 < ⋯ < b k ≤ n 1\leq b_1<b_2<\cdots <b_k\leq n 1b1<b2<<bkn,求和 a b 1 − a b 2 + a b 3 − a b 4 + ⋯ a_{b_1}-a_{b_2}+a_{b_3}-a_{b_4}+\cdots ab1ab2+ab3ab4+最大值。
题目解析
注意:这里挑选的下标不需要是连续的,只要符合下标递增即可。
记dp[i][0]表示在下标 i i i之前,最后一个元素为+时能得到的最大值
记dp[i][1]表示在下标 i i i之前,最后一个元素为-时能得到的最大值
初始化 dp[0][0] = store[0],dp[0][1] = 0
递推式
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , s t o r e [ i ] , d p [ i − 1 ] [ 1 ] + s t o r e [ i ] ) d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , 0 , d p [ i − 1 ] [ 0 ] − s t o r e [ i ] ) dp[i][0] = max(dp[i-1][0],store[i],dp[i-1][1]+store[i]) \\ dp[i][1] = max(dp[i-1][1],0,dp[i-1][0]-store[i]) dp[i][0]=max(dp[i1][0],store[i],dp[i1][1]+store[i])dp[i][1]=max(dp[i1][1],0,dp[i1][0]store[i])

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn = 3e5+5;

int t;
int n,q;
int store[maxn];
ll dp[maxn][2];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin>>t;
    while(t--){
        cin>>n>>q;
        rep(i,0,n){
            cin>>store[i];
        }
        dp[0][0] = store[0],dp[0][1] = 0;;
        rep(i,1,n){
            dp[i][0] = max(dp[i-1][1]+store[i],(ll)store[i]);
            dp[i][0] = max(dp[i][0],dp[i-1][0]);
            dp[i][1] = max(dp[i-1][0]-store[i],0LL);
            dp[i][1] = max(dp[i][1],dp[i-1][1]);
        }
        cout<<max(dp[n-1][0],dp[n-1][1])<<endl;
    }

    re 0;
}

Problem C2

三个小时的血泪教训,XOR不能交换值相同的变量

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

template<class T> void _deb(const char *name,T val){
    cout<<name<<val<<endl;
}
const int MAXN = 3e5+5;

int t;
int n,q;
ll ans;
int store[MAXN];
set<int> localMax,localMin;
void viewModel(int pos);
inline bool isLocalMax(int pos);
inline bool isLocalMin(int pos);
void checkLocal(int num);
void LocalMax(int pos);
void LocalMin(int pos);
int main(){

    scanf("%d",&t);
    while(t--){
        ans = 0;
        localMax.clear();localMin.clear();

        scanf("%d %d",&n,&q);
        rep(i,1,n+1)
            scanf("%d",&store[i]);
        store[0] = store[n+1] = -1;
        rep(i,1,n+1)
            viewModel(i);
        printf("%lld\n",ans);
        rep(i,0,q){
            int posA,posB;
            scanf("%d %d",&posA,&posB);
            //swap
            swap(store[posA],store[posB]);
            rep(_,max(posA-1,1),min(posA+2,n+1)){
                checkLocal(store[_]);
                viewModel(_);
            }
            rep(_,max(posB-1,1),min(posB+2,n+1)){
                checkLocal(store[_]);
                viewModel(_);
            }
            printf("%lld\n",ans);
        }
    }

    re 0;
}
void viewModel(int pos){
    if(isLocalMax(pos))
        LocalMax(pos);
    if(isLocalMin(pos)){
        LocalMin(pos);
    }
}
inline bool isLocalMax(int pos){
    return store[pos]>store[pos-1] && store[pos]>store[pos+1];
}
inline bool isLocalMin(int pos){
    return store[pos]<store[pos-1] && store[pos]<store[pos+1];
}
void checkLocal(int num){
    if(localMin.find(num)!=localMin.end()){
        localMin.erase(num);
        ans += num;
    }
    if(localMax.find(num)!=localMax.end()){
        localMax.erase(num);
        ans -= num;
    }
}
void LocalMax(int pos){
    int &num = store[pos];
    localMax.insert(num);
    ans += num;
}
void LocalMin(int pos){
    int &num = store[pos];
    localMin.insert(num);
    ans -= num;
}

Problem D
题目描述
给你n个灯,你知道每个灯亮的时间,现在需要k个灯同时亮,问有多少种可能的方案
题目解析
深深折服于我弱鸡一样的代码实现
定义灯的 l l l为开始时间, r r r为结束时间。
按时间遍历,到达某个开始时间将灯加入集合,并计算包含加入元素的所有排列方式;到达结束时间则将灯从集合中移除。
按照以上思想,我们可以首先将灯按开始时间升序排序(结束时间可以不做处理),且由于仅在开始时间计算数值,所以不必遍历全部时间,只需要处理所有的开始时间点即可。
由于我们按升序加入灯,所以集合中结束时间越早的灯越容易被移除,故选择最小堆维护集合。
在每次加入前,将集合中已经熄灭的灯移除,再加入新灯,计算与此相关的所有排列数。

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

const int maxn = 3e5+5;
const int MOD = 998244353;
const int MAXN = 3e5+5;

LL da[MAXN];//G++ long long
void init()
{
    int i;
    da[0]=1;
    da[1]=1;
    for(i=2;i<MAXN;i++)
        da[i]=i*da[i-1]%MOD;
}
LL quickmod(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)
        {
            ans=(ans*a)%MOD;
            b--;
        }
        b/=2;
        a=((a%MOD)*(a%MOD))%MOD;
    }
    return ans;
}
LL C(LL a, LL b) //C(a,b)
{
    if(b>a)
        re 0LL;
    return (da[a]%MOD)*(quickmod(da[b]*da[a-b]%MOD,MOD-2))%MOD;
}

struct Lamp{
    int start,finish;
};

int n,k;
Lamp lamp[maxn];
priority_queue<int,vector<int>,greater<int> > Q;
inline bool cmp(const Lamp &a,const Lamp &b);
int main(){

    init();
    scanf("%d %d",&n,&k);
    rep(i,0,n){
        scanf("%d %d",&lamp[i].start,&lamp[i].finish);
    }
    sort(lamp,lamp+n,cmp);
    int ans = 0,i = 0;
    while(i<n){
        int nowTime = lamp[i].start;
        while(!Q.empty() && Q.top()<nowTime){
            Q.pop();
        }
        int num = Q.size();
        Q.push(lamp[i++].finish);
        ans = (ans + C((LL)num,(LL)k-1))%MOD;
    }

    printf("%d\n",ans);
    re 0;
}
inline bool cmp(const Lamp &a,const Lamp &b){
	re a.start<b.start;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值