Codeforces Round 1011 (Div. 2)
打的依托,A一直错,B写的好像麻烦了
A略
B
最简单的想法是:保证最后一步操作时所有数非0,我们可以将序列化为左右两部分,哪一部分有0则这一部分进行一次操作,最后这两部分进行一次操作
C
首先,x和y相等直接不可能。观察式子发现如果(x+k)xor(y+k)中的xor发挥的作用和+一样,那么左右两边相等,即x+k和y+k的二进制下的1不出现在同一位上。我们让x和y较大的那个加一个数能够最高位进一位,其他位为0,那么那个小的数即使加上最高位也不到大数加后最高位,此时同一位最多出现一个1
D
首先贪心分析会吃掉n/(k+1)盘寿司,倒数第i盘寿司最晚吃的时间是n-i*(k+1)+1,最早吃的时间是1.相当于在n个数中选择n/k个数,倒数第i个数的选择范围是(1,n-i*(k+1)+1),且每个数只能被选择一次,使得选择的数的总和最大,因为前面的选择范围更小,我们从前往后选,用线段树找到该范围下最大的寿司的编号,累加,再把这个寿司改成0
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int T,n,a[N],k,ans;
struct Tree{
int maxn,l,r,id;
}t[N*4];
void init()
{
ans=0;
for(int i=1;i<=4*n;i++)
t[i].l=t[i].r=t[i].maxn=t[i].id=0;
}
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r;
if(l==r) {t[p].maxn=a[l]; t[p].id=l; return ;}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
if(t[p*2].maxn>t[p*2+1].maxn)
{
t[p].maxn=t[p*2].maxn;
t[p].id=t[p*2].id;
}
else
{
t[p].maxn=t[p*2+1].maxn;
t[p].id=t[p*2+1].id;
}
}
int ask(int p,int l,int r)
{
if(t[p].l>=l&&t[p].r<=r) return t[p].id;
int mid=(t[p].l+t[p].r)/2;
int s1=0,s2=0;
if(l<=mid) s1=ask(p*2,l,r);
if(r>mid) s2=ask(p*2+1,l,r);
if(a[s1]>a[s2]) return s1;
else return s2;
}
void change(int p,int k)
{
if(t[p].l==t[p].r) {t[p].maxn=0; return;}
int mid=(t[p].l+t[p].r)/2;
if(k<=mid) change(p*2,k);
else change(p*2+1,k);
if(t[p*2].maxn>t[p*2+1].maxn)
{
t[p].maxn=t[p*2].maxn;
t[p].id=t[p*2].id;
}
else
{
t[p].maxn=t[p*2+1].maxn;
t[p].id=t[p*2+1].id;
}
}
void solve()
{
cin>>n>>k;
init();
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
for(int i=n/(k+1);i>=1;i--)
{
int t=ask(1,1,n-i*(k+1)+1);
ans+=a[t];
change(1,t);
}
cout<<ans<<endl;
}
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--) solve();
}