Codeforces Round 882 (Div. 2) A-D题解
A. The Man who became a God
题意:定义f(l,r)为区间[l,r]所有相邻数的差的绝对值的和,大小为1的区间的f为0,给出一个数组a,问把他分成m个区间,这m个区间的f值的和最小是多少
Solution
我们可以最开始就把它看成一个完整的,每分成一段就会删除一个绝对值之差,那么把最大的挑出来删掉即可
void solve()
{
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
priority_queue<int,vector<int>,less<int> >q;
for(int i=1;i<n;i++)q.push(abs(a[i]-a[i+1]));
int temp=m-1;
while(temp--)
{
q.pop();
}
int ans=0;
while(q.size())
{
ans+=q.top();
q.pop();
}
cout<<ans<<"\n";
}
B. Hamon Odyssey
题意:dio有一个吸血鬼军队,吸血鬼的力量是一个数组a,在区间[l,r]内的吸血鬼可以变为一个组,该组的力量为二进制与运算之和,现在要把吸血鬼军队整体的力量最小化,问最小化的情况下可以分为多少组
Solution
首先明白一个定理,二进制与运算只会减少和不变,不会增加,于是乎我们可以找到最小值
如果这个最小值是一个正数,那么要最小化吸血鬼军队的力量,只能出现一个组
如果这个最小值是0,那么才可以出现多个组
如果要出现多个组的话,只需从头开始找二进制与运算之和为0的区间即可
void solve()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int now=a[1];
for(int i=2;i<=n;i++)
{
now&=a[i];
}
int minn=now;
now=(1<<30)-1;
int ans=0;
for(int i=1;i<=n;i++)
{
now&=a[i];
if(now==minn)
{
ans++;
now=(1<<30)-1;
//cout<<i<<"\n";
}
}
/*int xx=1e9;
now=(1<<30)-1;
cout<<(xx&now)<<"\n";*/
if(minn==0)cout<<ans<<"\n";
else cout<<1<<"\n";
}
C. Vampiric Powers, anyone?
题意:dio现在有m个吸血鬼,这些吸血鬼的力量是一个数组a,他可以无限制造吸血鬼,制造一个吸血鬼首先要选定一个在区间[1,m]之间的数i,然后该吸血鬼的力量等于sum
这个吸血鬼会变成最后一个吸血鬼(即m的大小会加一)
问dio最终拥有的吸血鬼的力量最大值是多少
Solution
我们可以发现,如果我们先选一个i,制造一个吸血鬼,再选一个小于i的数j,那么第二次会得到一个力量为[j,i]区间异或和的吸血鬼
因为异或相同的数会抵消为0
选择一个大于i的数也是
那么答案就是区间异或和的最大值
这题的数最大为28-1,所以我们可以用一个数组把是否出现过数字存下来,然后挨个遍历即可
受不了,赛时没想到这个数组数的范围有啥用QAQ
void solve()
{
int n;cin>>n;
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int now=0;
int ans=0;
//cout<<(1<<8)-1<<"\n";
p[0]=1;
for(int i=1;i<=n;i++)
{
now^=a[i];
p[now]=1;
for(int j=0;j<(1<<8);j++)
{
if(p[j])ans=max(ans,j^now);
}
}
cout<<ans<<"\n";
}
D. Professor Higashikata
题意:给出一个长为n的01串,从中选m个区间,按照选择的顺序拼接成一个新的字符串,现在有q次对原字符串的更新,每次更新可以将一个位置上的0变为1,1变为0,我们可以进行任意次操作,每次操作可以交换原字符串上的任意两个数,问每次更新后,需要多少次交换可以使得它的字典序最大(更新对字符串是有效的,而交换操作是不会带到下一次更新上的)
Solution
线段树+优先队列+树状数组
我也不知道我为什么写的这么复杂
但是它过了...它过了...它过了...它过了...它过了...它过了...它过了...
emmmmmmm...
总之是能用的
思路是这样的
我们考虑到字典序最大,那么1越在前面肯定是越好的
那么统计一下整个字符串上的1的数量,然后将其往前挪即可
再就是拼接后的字符串,其实我们只用看这个字符串上的第一次出现过的数的位置,把它排个序即可
比如样例2的[5,6],[2,3],[6,8],[5,7],[5,8],[6,8],排完序之后就是5,6,2,3,7,8
线段树+优先队列处理第一次出现过的数的位置
然后用树状数组统计一下拼接后的字符串上对应位置的1的数量的前缀和
每次操作的时候查询一下即可
说实话,写晕了,很丑,不推荐看
int a[N];
int p[N];
int t[N<<2];
void build(int l,int r,int rt)
{
if(l==r)
{
t[rt]=r-l+1;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,(rt<<1)|1);
t[rt]=t[rt<<1]+t[(rt<<1)|1];
}
void update(int l,int r,int L,int R,int x,int rt)
{
if(L<=l&&r<=R)
{
if(t[rt]!=0)
{
int mid=(l+r)>>1;
if(t[rt]!=r-l+1)
{
if(t[rt<<1]!=0)update(l,mid,L,R,x,rt<<1);
if(t[(rt<<1)|1]!=0)update(mid+1,r,L,R,x,(rt<<1)|1);
}else
{
for(int i=l;i<=r;i++)if(p[i]==-1)p[i]=x;
}
t[rt]=0;
}
return;
}
int mid=(l+r)>>1;
if(mid>=L)update(l,mid,L,R,x,rt<<1);
if(mid+1<=R)update(mid+1,r,L,R,x,(rt<<1)|1);
t[rt]=t[rt<<1]+t[(rt<<1)|1];
}
struct node
{
int x,y;
bool operator <(const node&t)const &{
if(x!=t.x)return x > t.x;
return y > t.y;
}
}e[N];
int cnt1[N]; //用于存放出现在拼接后的字符串中的第一次出现的位置
int t1[N]; //树状数组
int cnt=0;
void add(int k,int x){
while(k<=cnt){//n为数组大小
t1[k]+=x;
k+=lowbit(k);
}
}
int getsum(int i){
int res=0;
while(i){//到数组头结束
res+=t1[i];
i-=lowbit(i);
}
return res;
}
void solve()
{
memset(p,-1,sizeof(p));
int n,m,q;cin>>n>>m>>q;
string s;cin>>s;
build(1,n,1);
for(int i=1;i<=m;i++)
{
int l,r;cin>>l>>r;
update(1,n,l,r,i,1);
}
priority_queue<node>qq;
int num2=0;//表示外面1的数量
for(int i=1;i<=n;i++)
{
if(p[i]!=-1)qq.push({p[i],i});
else
{
if(s[i-1]=='1')num2++;
}
}
//cout<<qq.size()<<"\n";;
cnt=qq.size();
int temp=0;
while(qq.size())
{
node t=qq.top();
qq.pop();
cnt1[t.y]=++temp;
add(temp,(s[t.y-1]-'0'));
}
while(q--)
{
int x;cin>>x;
if(cnt1[x]==0)
{
if(s[x-1]=='0')num2++;
else num2--;
}
s[x-1]=((s[x-1]-'0')^1)+'0';
if(cnt1[x])add(cnt1[x],2*(s[(x-1)]-'0')-1);
int res=getsum(cnt);//表示拼接后的字符串中1的数量
if(res+num2<=cnt)
{
int ii=getsum(num2+res); //表示前(1的总个数)个中1的数量
cout<<res+num2-ii<<"\n";
}else
{
cout<<cnt-res<<"\n";
}
}
}

浙公网安备 33010602011771号