老玩家回归 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
但要注意删除的时候删一个指针,不要删元素,否则还是会把相等的都删掉。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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* */
实际上,逆向考虑一下
set中的最后一堆才直接决定答案,那么这个堆是怎么来的?是若干相邻元素并出来的。还原到原始序列就是连续的一段。而且这个段活到了最后,这表明这一段是所有段中绝对值最大的。
所以,找原序列的最大子段和、最小子段和再对绝对值取max即可。
orz xls