CF1329B Dreamoonはシーケンスが好き
タイトルの説明
シーケンスは、シーケンスが空でなく単調に増加し、接頭辞XORおよび単調に増加する場合にのみ適格です。\(d、m \)(\(d、m \ leq 10 ^ 9 \))が
与えられた場合、シーケンス内のすべての数値に対して、各数値が\(d \)を超えない正の整数であることを確認します。数値係数\(m \)とは何ですか。
解決策
有効なシーケンスの長さが\(n \)で、シーケンスが\(a_1、a_2、...、a_n \)であり、接頭辞の合計が\(b_1、b_2、...、b_n \)であると仮定します。\(\ {b_n \} \)
を増やしたい場合は、\(\ forall i、b_i <b_ {i + 1} \)が必要です。\(b_i <b_ {i + 1} \) 2つのケースがあります:1. \(b_ {i + 1} \)の最上位バイナリビットが\(b_i \)よりも高い; 2.両方の最上位バイナリビット同じで、両方の最高ビットを差し引いた後、\(b_ {i + 1} \)の方が大きくなります。第二のケースを考える、の両方が見られる場合により、最上位ビットと同じ\(A_ {I + 1} = b_i \空間XOR \空間B_ {I + 1} \) 利用できる(\ A_ {iは、+ 1} \ )\(b_i \)よりも低い最上位ビットを持ち、\(a_1、...、a_i \)の少なくとも1つの最上位ビットは\(b_i \)と同じで、\(\ {a_n \} \)と同じです。単一増加の条件が一致しません。したがって、2番目の状況はありません。つまり、\(\ {b_n \} \)
の各数値の最上位のバイナリビットは、前の数値よりも高くなっています。\(\ {a_n \} \)の各数値の最上位の2進数は、前の数値よりも大きい
と結論付けることができます。\(1,2、...、D \ ) 番号が最も高いビットに従って分類しました。カテゴリごとに1つを選択するかどうかを選択できます。
コード
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define LL long long
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
char ch[20];int f=0;
if(!x){putchar('0'),putchar('\n');return;}
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int t,d,m,ans;
int mo(int x){if(x>=m)return x-m;if(x<0)return x+m;return x;}
int main()
{
t=read();
while(t--)
{
d=read(),m=read();ans=1;
LL now=1,nxt=1;
while(1)
{
if(nxt>=d){ans=(LL)ans*(d-now+2)%m;break;}
ans=(LL)ans*(nxt-now+2)%m,now=nxt+1;nxt=(now<<1)-1;
}
ans=mo(ans-1);
write(ans);
}
return (~(0-0)+1);
}
CF1329Cブラジルはヒープが好き
タイトルの説明
高さ\(h \)のやや重み付けされた完全なバイナリツリーがあり、これは大きなルートヒープの性質を満たします。
葉ではない点を削除します。重みの大きい息子がその点の位置に移動し、元の位置で繰り返します。
一部のポイントを削除するには、高さ\(g \)の完全なバイナリツリーにします。残りのポイントのポイントウェイトは可能な限り小さくなります。最小ポイントの重みと、削除するポイントを尋ねます。
すべてのポイントには異なるポイントの重み\(g \ leq h \ leq 20 \)があります。
解決策
まず、各ポイントのポイントの重みは削除後のアトミックツリーの最大値になるため、残りのパーツのポイントの重みはポイントの削除の順序とは関係がないことがわかります。
貪欲な構築方法を検討してください。下から上に構築します。これにより、各ポイントの新しいポイントの重みは、そのポイントの周りの息子の新しいポイントの重みよりも大きい、原子ツリー内の可能な最小のポイントの重みになります。
ポイントの重みを可能な限り小さく選択しても、ポイントの祖先の下限は大きくなりません。
サブツリーのポイントウェイト配列を各ポイントのサイズ順に維持することが可能で、ポイントの配列は、同様のソート方法で左右の息子の配列によってマージできます。時間の複雑さ\(\ Theta(2 ^ h \ times h)\)。
ポイントの重みを見つけるために毎回ポイントをトラバースするすべての息子を使用する場合、時間の複雑さも\(\ Theta(2 ^ h \ times h)\)ですが、再帰定数はわずかに大きくなります。
この問題には、完全に直感的な貪欲なアプローチがもう1つあります。最も近いリーフからルートまでの距離が\(g \)になるまで現在のルートの値を削除し、次に左右のサブツリーを再帰的に処理します。
最大値はヒープの最上部にあり、ポイントとそのすべての祖先を削除できない場合、その左と右のサブツリー間に影響はありません。時間の複雑さも\(\シータ(2 ^ h \回h)\)です。
コード
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define LL long long
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
using namespace std;
#define maxn ((1<<20)+7)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define ls (u<<1)
#define rs (u<<1|1)
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(LL x)
{
char ch[20];int f=0;
if(!x){putchar('0'),putchar('\n');return;}
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int t,n,h,g,a[maxn],yes[maxn],len,dfn[maxn],siz[maxn],val[maxn];
LL ans;
pii ar[maxn],tmp[maxn];
void merge(int u)
{
int nl=dfn[ls],nr=dfn[rs],c=0;
rep(i,1,siz[u])
{
int va=(nl>=dfn[ls]+siz[ls]?1e9:ar[nl].fi),
vb=(nr>=dfn[rs]+siz[rs]?1e9:ar[nr].fi),
vc=c?1e9:a[u];
if(va<min(vb,vc)){tmp[i].fi=va,tmp[i].se=ar[nl].se,nl++;}
else if(vb<min(va,vc)){tmp[i].fi=vb,tmp[i].se=ar[nr].se,nr++;}
else tmp[i].fi=a[u],tmp[i].se=u,c++;
}
rep(i,1,siz[u])ar[dfn[u]+i-1]=tmp[i];
}
int getv(int u,int li)
{
int L=dfn[u],R=dfn[u]+siz[u]-1;pii res;res.fi=1e9;
while(L<=R)
{
int mid=(L+R>>1);
if(ar[mid].fi>li)
{
if(res.fi>ar[mid].fi)res=ar[mid];
R=mid-1;
}
else L=mid+1;
}
yes[res.se]=1;
return res.fi;
}
void work(int u,int d)
{
if(d==g)
{
sort(ar+dfn[u],ar+dfn[u]+siz[u]);
val[u]=ar[dfn[u]].fi,yes[ar[dfn[u]].se]=1,ans+=val[u];
return;
}
work(ls,d+1),work(rs,d+1),merge(u);
val[u]=getv(u,max(val[ls],val[rs])),ans+=val[u];
return;
}
void build(int u,int d)
{
dfn[u]=++len,ar[len].fi=a[u],ar[len].se=u,siz[u]=1;
if(d==h){return;}
build(ls,d+1),build(rs,d+1),siz[u]+=siz[ls]+siz[rs];
return;
}
int main()
{
t=read();
while(t--)
{
h=read(),g=read(),n=(1<<h)-1;ans=len=0;
rep(i,1,n)a[i]=read(),yes[i]=0;
build(1,1);
work(1,1);
write(ans);
dwn(i,n,1)if(!yes[i])printf("%d ",i);
puts("");
}
return (~(0-0)+1);
}
いくつかの考え
射線を越えたときに黒くならないでください。射線を越えた人とチームを組んでレベルを向上させることはできず、あまり強くない人とチームを組んでスコアを上げることはできません。
ただし、英語力のある人とハッキングすることで、レベル(翻訳するのではなく、寝る前に考える時間が増えること)とスコアを向上させること(質問について考える時間を増やすことのほうが簡単です)の両方を改善できます。:)