老玩家回归 Codeforces Round #843 (Div. 2)

cf水平跌至NOIP前期。。

https://codeforces.com/contest/1775

 

 A1 A2就是分类讨论结论题

但还是想了半天。。。

 

B还是结论题。

如果一个数的某一位是所有数中“独有”的,那么这个数称为关键数。如果所有数都是关键数,那么No。否则Yes

原因:

如果一个数x是关键数,那么A集合选择x,B集合也一定要选择x,所以最终会A=B

否则如果存在一个数不是关键数,那么A集合选择所有数,B集合选择所有数再删掉那个非关键数,即可。

 

C题,连着取&,而且加1,考虑进位

m恰好让(n^x)中最高的位是0就行。算出这个m

这里要联系n去做,不要只在(n^x)上去做,因为n还有一些低位有1

然后看m&n是不是x就行了 。因为m是和n 01位置差距最大的。只要m&n=x就行

 

D题,只有质因子重要,然后质因数分解,对每个质因子建立一个vector<int>,vector里面是所有拥有这个质因子的节点

从s开始BFS,每次新加入queue中的是队首元素所含所有质因子的vector中的节点,如果一个质因子遍历过了,那么这个vector就不用再访问了,如果一个节点进入过了,也不用再访问了。

记录一个前驱信息就可以输出方案了。

 

E题,

题意:n个数的序列,每次选择一个子序列,把在这个子序列中下标为奇数的数+1,偶数的数-1,或者反之。问至少多少次可以让原序列变全0

可以通过反证法得知,让正数再+1和负数-1是不优的。

证明的话,考虑把所有连续的非负数和负数堆在一起,这样得到正负交替的数列。

如果正数堆+1又-1,那么这个堆是不变的,所以这种操作没有意义。如果正数堆+1,其他的正数堆-1,那么不如不让正数堆+1,把+1分给中间的一些负数堆,显然更优。

具体做的时候,也考虑正负交替的数列

一个显然正确的贪心做法是,每次选择所有堆进行处理+1-1处理,最终会把绝对值最小的堆抹掉,然后把被抹掉的堆的相邻两个堆合并成新堆,如此反复直到都抹掉即可。

实现时,使用两个set,每个set都是存(pos,val)的pair,pos表示堆的位置,val表示数值

第一个set按照pos递增存,第二个set按照val递增存,这样就可以处理找最小堆抹掉并合并相邻两个的操作了。

+1-1的时候是所有的数都处理,所以用一个懒标记记录每个数删了多少就行了(实际上懒标记就是ans)

 

我犯的错:
1. fl和f1写混了。。。。。。。之后还是用flag当变量名比较合适。

2. 初始化每一堆操作的pos出错了。。。

3. 对于set,如果这样重载小于号:

bool friend operator<(cur a,cur b){
            return a.val<b.val;
        }

那么当a和b的val一样的时候,就会认为相同,根本不管pos是不是一样。导致被去重了。

所以为了保留应该这样:

bool friend operator<(cur a,cur b){
            if(a.val!=b.val) return a.val<b.val;
            return a.pos<b.pos;
        }

当然,使用第一种重载小于号的时候,我们也可以用multiset

但要注意删除的时候删一个指针,不要删元素,否则还是会把相等的都删掉。

 

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}
namespace Modulo{
    const int mod=998244353;
    il int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    il int sub(int x,int y){return ad(x,mod-y);}
    il int mul(int x,int y){return (ll)x*y%mod;}
    il void inc(int &x,int y){x=ad(x,y);}
    il void inc2(int &x,int y){x=mul(x,y);}
    il int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
    template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);}
    template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);}
}
#define int long long
//using namespace Modulo;
namespace Miracle{
    const int N=2e5+5;
    int n;
    int a[N];
    struct node{
        int pos;
        ll val;
        node(){}
        node(int p,ll v){
            pos=p;
            val=v;
        }
        bool friend operator<(node a,node b){
            return a.pos<b.pos;
        }
    };
    struct cur{
        int pos;
        ll val;
        cur(){}
        cur(int p,ll v){
            pos=p;
            val=v;
        }
        cur(node t){
            pos=t.pos;
            val=t.val;
        }
        bool friend operator<(cur a,cur b){
            if(a.val!=b.val) return a.val<b.val;
            return a.pos<b.pos;
        }
    };
    set<node>mySet;
    multiset<cur>values;

    int main(){
        int t;
        rd(t);
        while(t--){
            mySet.clear();
            values.clear();
            rd(n);
            for(int i=1;i<=n;++i){
                rd(a[i]);
            }
            ll sum=0;
            int fl=0;
            for(int i=1;i<=n;++i){
                if(i==1) fl=(a[i]>=0);
                if((a[i]>=0)==fl){
                    sum+=a[i];
                }else{
                    fl=(a[i]>=0);
                    mySet.insert(node(i-1,abs(sum)));
                    values.insert(cur(i-1,abs(sum)));
                    sum=a[i];
                }
            }
            mySet.insert(node(n,abs(sum)));
            values.insert(cur(n,abs(sum)));
            ll ans=0;
            while(!values.empty()){
                auto now=*values.begin();
                node tmp=node(now.pos,now.val);
                auto le=mySet.lower_bound(tmp);
                auto ri=mySet.upper_bound(tmp);
                bool f1=false,f2=false;
                if(le!=mySet.begin()){
                    --le;
                    f1=true;
                }
                ll old=ans;
                ll ad=tmp.val-ans;
                ans+=ad;
                if(ri!=mySet.end()) f2=true;
                if(f1&&f2){
                    auto nwnode=node(le->pos,(ri->val-old+le->val-old-2*ad+ans));
                    auto LE=*le;
                    auto RI=*ri;
                    mySet.erase(LE);
                    mySet.erase(RI);
                    mySet.insert(nwnode);
                    values.erase(cur(LE));
                    values.erase(cur(RI));
                    values.insert(cur(nwnode));
                }else if(f1){

                }else if(f2){

                }
                mySet.erase(tmp);
                values.erase(now);
            }
            printf("%lld\n",ans);
        }
        return 0;
    }
}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/
View Code

 

实际上,逆向考虑一下

set中的最后一堆才直接决定答案,那么这个堆是怎么来的?是若干相邻元素并出来的。还原到原始序列就是连续的一段。而且这个段活到了最后,这表明这一段是所有段中绝对值最大的。

所以,找原序列的最大子段和、最小子段和再对绝对值取max即可。

orz xls

posted @ 2023-01-11 16:25  *Miracle*  阅读(129)  评论(4编辑  收藏  举报