动态求连续区间和
题目描述:
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。
输入格式:
第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。
第二行包含 n 个整数,表示完整数列。
接下来 m 行,每行包含三个整数 k,a,b(k=0,表示求子数列[a,b]的和;k=1,表示第 a个数加 b)。
数列从 1 开始计数。
输出格式:
输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。
数据范围:
1≤n≤100000
1≤m≤100000
1≤a≤b≤n
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],tr[N];
int lowbit(int x){
return x&-x;
}
void add(int a, int b) {
for (int i = a; i <= n; i += lowbit(i)) {//包含本节点的后续节点,全部加上本节点的变化量,初始化时,从i=1开始,每个节点都被前面节点所求和
tr[i] += b;
}
}
int query(int y) {
int res = 0;
for (int i = y; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
add(i,a[i]); //初始情况每个节点本身都是0,要使得每个节点从0变成a[i],需要加上a[i],并累积tr[i]
}
while(m--){
int k,a,b;
cin>>k>>a>>b;
if(k==1){
add(a,b);
}
else{
cout<<query(b)-query(a-1)<<endl;
}
}
}
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int w[N];//记录一下权重 //左儿子 u<<1;右儿子u<<1|1;
struct node{
int l,r;
int sum;
}tr[N*4];
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void build(int u,int l,int r){ //节点编号,左区间,右区间
if(l==r){
tr[u]={l,r,w[r]}; //叶子节点直接赋值
}
else{
//对于父节点,分区间,先赋值
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
int query(int u,int l,int r)//从根节点找到叶子节点
{ if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
int mid=tr[u].l+tr[u].r>>1;
int sum=0;
if(mid>=l){ //左端点与区间是否重合
sum+=query(u<<1,l,r);
}
if(r>=mid+1){
sum+=query(u<<1|1,l,r);
}
return sum;
}
void modify(int u,int x,int v){// 编号,位置,加的数
if(tr[u].l==tr[u].r){
tr[u].sum+=v;
}
else{
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid){
modify(u<<1,x,v);
}
else{
modify(u<<1|1,x,v);
}
pushup(u); //更新
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
build(1,1,n);/*第一个参数是根节点的下标,根节点是一号点,然后初始区间是 1 到 n */
//后面的话就是一些修改操作了
while(m--)
{
int k,a,b;
scanf("%d%d%d",&k,&a,&b);
if(!k)printf("%d\n",query(1,a,b));//求和的时候,也是传三个参数,第一个的话是根节点的编号 ,第二个的话是我们查询的区间
//第一个参数也就是当前节点的编号
else
modify(1,a,b);//第一个参数是根节点的下标,第二个参数是要修改的位置,第三个参数是要修改的值
}
}
1265. 数星星
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。
如果一个星星的左下方(包含正左和正下)有 k� 颗星星,就说这颗星星是 k� 级的。
例如,上图中星星 5是 3 级的(1,2,4在它左下),星星 2,4 是 11 级的。
例图中有 1个 0 级,2 个 1级,1 个 2 级,1 个 33级的星星。
给定星星的位置,输出各级星星的数目。
换句话说,给定 N� 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。
输入格式
第一行一个整数 N�,表示星星的数目;
接下来 N� 行给出每颗星星的坐标,坐标用两个整数 x,y�,� 表示;
不会有星星重叠。星星按 y� 坐标增序给出,y� 坐标相同的按 x� 坐标增序给出。
输出格式
N� 行,每行一个整数,分别是 00 级,11 级,22 级,……,N−1�−1 级的星星的数目。
数据范围
1≤N≤15000,
0≤x,y≤32000
输入样例:
5
1 1
5 1
7 1
3 3
5 5
输出样例:
1
2
1
1
0
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=32010;
int a[N],tri[N],level[N];//第i级星的数目 <=n;
int lowbit(int x){
return x&-x;
}
void add(int x){
for(int i=x;i<=N;i+=lowbit(i)){
tri[i]++;
}
}
int sum(int x){
int ans=0;
for(int i=x;i>0;i-=lowbit(i)){
ans+=tri[i];
}
return ans;
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
int x, y;
cin>>x>>y;
x++;
level[sum(x)]++;
add(x);
}
for(int i=0;i<n;i++){
cout<<level[i]<<endl;
}
}
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100010;
struct node{
int l,r;
int maxv;
}tr[4*N];
int w[N],n,m;
void build(int u,int l,int r){
if(l==r){
tr[l]={l,r,w[u]};
}else{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
tr[u].maxv=max(tr[u<<1].maxv,tr[u<<1|1].maxv);
}
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].maxv;
}
int mid=tr[u].l+tr[u].r>>1;
int max1=-0x3f3f3f3f;
if(l<=mid){
max1=query(u<<1,l,r);
}
if(r>=mid+1)
max1=max(max1,query(u<<1|1,l,r));
return max1;
}
int main(){
int m,n;
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
}
build(1,1,n);
while(m--){
int a,b;
scanf("%d %d",&a,&b);
printf("%d\n",query(1,a,b));
}}
1215. 小朋友排队
n� 个小朋友站成一排。
现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。
开始的时候,所有小朋友的不高兴程度都是 00。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加 11,如果第二次要求他交换,则他的不高兴程度增加 22(即不高兴程度为 33),依次类推。当要求某个小朋友第 k� 次交换时,他的不高兴程度增加 k�。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
输入格式
输入的第一行包含一个整数 n�,表示小朋友的个数。
第二行包含 n� 个整数 H1,H2,…,Hn�1,�2,…,��,分别表示每个小朋友的身高。
输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
数据范围
1≤n≤1000001≤�≤100000,
0≤Hi≤10000000≤��≤1000000
输入样例:
3
3 2 1
输出样例:
9
样例解释
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N= 1000005;
int a[N],tr[N];
int sum[N];
int lowbit(int x){
return x&(-x);
}
void add(int x,int v){
for(int i=x;i<N;i+=lowbit(i))
{
tr[i]+=v;
}
}
int query(int x){
int an=0;
for(int i=x;i;i-=lowbit(i)){
an+=tr[i];
}
return an;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]++;
}
for(int i=1;i<=n;i++){
add(a[i],1);
sum[i]=query(N-1)-query(a[i]);//求前面比a[i]大的数
}
memset(tr,0,sizeof tr); //求后面比它小的数
for(int i=n;i>0;i--){
add(a[i],1);
sum[i]+=query(a[i]-1);
}
LL ans=0;
for(int i=1;i<=n;i++){
ans+=(LL)(1+sum[i])*sum[i]/2;
}
cout<<ans;
}
X星球的一批考古机器人正在一片废墟上考古。
该区域的地面坚硬如石、平整如镜。
管理人员为方便,建立了标准的直角坐标系。
每个机器人都各有特长、身怀绝技。
它们感兴趣的内容也不相同。
经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。
矩形的表示格式为 (x1,y1,x2,y2)(�1,�1,�2,�2),代表矩形的两个对角点坐标。
为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。
小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。
其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。
注意,各个矩形间可能重叠。
输入格式
第一行,一个整数 n�,表示有多少个矩形。
接下来的 n� 行,每行有 44 个整数 x1,y1,x2,y2�1,�1,�2,�2,空格分开,表示矩形的两个对角顶点坐标。
输出格式
一行一个整数,表示矩形覆盖的总面积。
数据范围
1≤n≤100001≤�≤10000,
0≤x1,x2,y2,y2≤100000≤�1,�2,�2,�2≤10000
数据保证 x1<x2�1<�2 且 y1<y2�1<�2。
输入样例1:
3
1 5 10 10
3 1 20 20
2 7 15 17
输出样例1:
340
输入样例2:
3
5 2 10 6
2 7 12 10
8 1 15 15
输出样例2:
128
先不做
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define x first
#define y second
using namespace std;
const int N=1e5+10;
typedef pair<int,int>PII;
int cnt[N];
bool st[N];
PII a[N];
int main(){
int n,d,k;
scanf("%d %d %d",&n,&d,&k);
for(int i=0;i<n;i++){
scanf("%d %d",&a[i].x,&a[i].y);
}
sort(a,a+n);
for(int i=0,j=0;i<n;i++){
int id=a[i].y;
cnt[id]++;
while(a[i].x-a[j].x>=d){
cnt[a[j].y]--;
j++;
}
if(cnt[id]>=k){
st[id]=1;
}
}
for(int i=0;i<N;i++){
if(st[i]){
cout<<i<<endl;
}
}
}
双指针
日志统计 — 蓝桥杯
题目描述
小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N 行。
其中每一行的格式是:
ts id
表示在 ts 时刻编号 id 的帖子收到一个”赞”。
现在小明想统计有哪些帖子曾经是”热帖”。
如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是”热帖”。
具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是”热帖”。
给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。
输入格式
第一行包含三个整数 N,D,K。
以下 N 行每行一条日志,包含两个整数 ts 和 id。
输出格式
按从小到大的顺序输出热帖 id。
每个 id 占一行。
数据范围
1≤K≤N≤105,
0≤ts,id≤105,
1≤D≤10000
输入样例:
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
1
2
3
4
5
6
7
8
输出样例:
1
3
1
2
思路// 排序+双指针
// 1.对所有的赞按照时间从小到大排序
// 2.通过双指针i,j维护长度不大于d的区间,并记录该区间的中所有帖子获得的赞数
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define x first
#define y second
using namespace std;
const int N=1e5+10;
typedef pair<int,int>PII;
int cnt[N];
bool st[N];
PII a[N];
int main(){
int n,d,k;
scanf("%d %d %d",&n,&d,&k);
for(int i=0;i<n;i++){
scanf("%d %d",&a[i].x,&a[i].y);
}
sort(a,a+n);
for(int i=0,j=0;i<n;i++){
int id=a[i].y;
cnt[id]++;
while(a[i].x-a[j].x>=d){ // 进入合理区间
cnt[a[j].y]--;
j++;
}
if(cnt[id]>=k){
st[id]=1;
}
}
for(int i=0;i<N;i++){
if(st[i]){
cout<<i<<endl;
}
}
}
bfs
献给阿尔吉侬的花束
阿尔吉侬是一只聪明又慵懒的小白鼠,它最擅长的就是走各种各样的迷宫。今天它要挑战一个非常大的迷宫,研究员们为了鼓励阿尔吉侬尽快到达终点,就在终点放了一块阿尔吉侬最喜欢的奶酪。现在研究员们想知道,如果阿尔吉侬足够聪明,它最少需要多少时间就能吃到奶酪。
迷宫用一个R×C的字符矩阵来表示。字符S表示阿尔吉侬所在的位置,字符E表示奶酪所在的位置,字符#表示墙壁,字符.表示可以通行。阿尔吉侬在1个单位时间内可以从当前的位置走到它上下左右四个方向上的任意一个位置,但不能走出地图边界。
输入
第一行是一个正整数T(1 ≤ T ≤ 10),表示一共有T组数据。
每一组数据的第一行包含了两个用空格分开的正整数R和C(2 ≤ R, C ≤ 200),表示地图是一个R×C的矩阵。
接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。
输出
对于每一组数据,输出阿尔吉侬吃到奶酪的最少单位时间。若阿尔吉侬无法吃到奶酪,则输出“oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。
样例
输入数据 1
3
3 4
.S..
###.
..E.
3 4
.S..
.E..
....
3 4
.S..
####
..E.
输出数据 1
5
1
oop!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define x first
#define y second
using namespace std;
const int N=210;
typedef pair<int,int>PII;
int dis[N][N];
char cur[N][N];
void bfs(PII start){
queue<PII>q;
q.push(start); //根节点入队
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
while(!q.empty()){ //取队头
PII u=q.front();
q.pop();
for(int i=0;i<4;i++){
int x=u.x+dx[i];
int y=u.y+dy[i];
if(cur[x][y]=='#') continue;
if(cur[x][y]=='.'){
dis[x][y]=dis[u.x][u.y]+1;
cur[x][y]='#';
q.push({x,y});
}
if(cur[x][y]=='E'){
cout<<dis[u.x][u.y]+1<<endl;
return ;
}
}
}
cout<<"oop!"<<endl; //判断子节点状态;
}
int main(){
int t;
cin>>t;
while(t--){
PII start;
memset(dis,0,sizeof dis);
memset(cur,'#',sizeof cur);
int n,m;
cin>>n>>m;
for(int i = 1; i <= n; i++)//从第一行存储地图,因为四周都是墙,bfs时,可以不做越界判断
{
for(int j = 1; j <= m; j++)//从第一;列存储地图,因为四周都是墙,bfs时,可以不做越界判断
{
cin >> cur[i][j];
if(cur[i][j] == 'S')//记录下起点位置。
{start={i,j}; cur[i][j] = '#';}
}
}
bfs(start);
}
return 0;
}
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
#define x first
#define y second
using namespace std;
const int N=210;
int r,c;
typedef pair<int,int>PII;
int dis[N][N];
char cur[N][N];
int bfs(PII start){
memset(dis,-1,sizeof dis);
dis[start.x][start.y]=0;
queue<PII>q;
q.push(start);
while(!q.empty()){
PII u=q.front();
q.pop();
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
for(int i=0;i<4;i++){
int x=u.x+dx[i];
int y=u.y+dy[i];
if(cur[x][y]=='#')continue;
if(x<0||x>=r||y<0||y>=c)continue;
if(dis[x][y]!=-1)continue; //遍历过
dis[x][y]=dis[u.x][u.y]+1;
q.push({x,y});
if(cur[x][y]=='E'){
return dis[x][y];
}
}
}
return -1;
}
int main(){
int t;
cin>>t;
while(t--){
PII start,end;
cin>>r>>c;
for(int i=0;i<r;i++){
scanf("%s",cur[i]);
}
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(cur[i][j]=='S')
start={i,j};
}
}
int dis1=bfs(start);
if(dis1==-1){
cout<<"oop!"<<endl;
}
else{
cout<<dis1<<endl;
}
}
}
1224. 交换瓶子
有N个瓶子,编号 1 ~ N,放在架子上。
比如有5个瓶子:
2 1 3 5 4
要求每次拿起2个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换2次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式为两行:
第一行: 一个正整数N(N<10000), 表示瓶子的数目
第二行:N个正整数,用空格分开,表示瓶子目前的排列情况。
输出数据为一行一个正整数,表示至少交换多少次,才能完成排序。
例如,输入:
5
3 1 2 5 4
程序应该输出:
3
再例如,输入:
5
5 4 3 2 1
程序应该输出:
2
法1暴力做法
从第一个瓶子开始,如果位置对直接下一个
否则 把瓶子上相应数的瓶子交换过来,交换次数加一
继续这个过程直到当前位置上的数正确;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+1;
int a[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int ans=0;
for(int i=1;i<=n;i++){
while(a[i]!=i){
int t=a[a[i]];
a[a[i]]=a[i];
a[i]=t;
ans++;
//a[i]位置
}
}
cout<<ans<<endl;
}
利用图论的知识,31254,可以看成两个环,3->2->1->3 5->4->5
交换31 产生三个环 3->2->3 1->1 5->4->5
对于每次交换两个数,最好的情况下可以使得环的数目减一,而最终目的是使得n个数各自产生指向自己的环。 假设刚开始有k个环,最少需要交换n-k次,可以求得答案
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+1;
int a[N]; //表示 第i个位置上的数指向a[i]的位置
bool st[N];//表示第i个位置有没有进入环;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int ans=0;
int cnt=0;
for(int i=1;i<=n;i++){
while(!st[i]){
cnt++;
for(int j=i;!st[j];j=a[j]){
st[j]=1;
}
}
}
cout<<n-cnt<<endl;
}
AcWing 1096. 地牢大师
题目链接
题目描述
你现在被困在一个三维地牢中,需要找到最快脱离的出路!
地牢由若干个单位立方体组成,其中部分不含岩石障碍可以直接通过,部分包含岩石障碍无法通过。
向北,向南,向东,向西,向上或向下移动一个单元距离均需要一分钟。
你不能沿对角线移动,迷宫边界都是坚硬的岩石,你不能走出边界范围。
请问,你有可能逃脱吗?
如果可以,需要多长时间?
输入格式
输入包含多组测试数据。
每组数据第一行包含三个整数 L,R,C 分别表示地牢层数,以及每一层地牢的行数和列数。
接下来是 L 个 R 行 C 列的字符矩阵,用来表示每一层地牢的具体状况。
每个字符用来描述一个地牢单元的具体状况。
其中, 充满岩石障碍的单元格用”#”表示,不含障碍的空单元格用”.”表示,你的起始位置用”S”表示,终点用”E”表示。
每一个字符矩阵后面都会包含一个空行。
当输入一行为”0 0 0”时,表示输入终止。
输出格式
每组数据输出一个结果,每个结果占一行。
如果能够逃脱地牢,则输出”Escaped in x minute(s).”,其中X为逃脱所需最短时间。
如果不能逃脱地牢,则输出”Trapped!”。
数据范围
1≤L,R,C≤100
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int l, r, c; // 分别是层数,每层的行数、列数
int res[N][N][N]; // 计算步数,顺便作为标记
char g[N][N][N]; // 建图
struct Point {
int z, x, y;
} st, ed;
bool check(int z, int x, int y) {
if (z < 1 || z > l) {
return false;
}
if (x < 1 || x > r) {
return false;
}
if (y < 1 || y > c) {
return false;
}
if (res[z][x][y] != -1) {
return false;
}
if (g[z][x][y] == '#') {
return false;
}
return true;
}
void bfs() {
queue<Point>w;
w.push(st);
res[st.z][st.x][st.y]=0;
while((int)w.size()>0){
auto t=w.front();
w.pop();
int dx[6] = {1, 0, -1, 0, 0, 0};
int dy[6] = {0, 1, 0, -1, 0, 0};
int dz[6] = {0, 0, 0, 0, -1, 1};
for (int i = 0; i < 6; i++) {
int z = t.z + dz[i];
int x = t.x + dx[i];
int y = t.y + dy[i];
if (check(z, x, y)) {
w.push({z, x, y});
res[z][x][y] = res[t.z][t.x][t.y] + 1;
}
}}}
int main(){
while(scanf("%d%d%d", &l, &r, &c) && l){
// 因为有多组输入,每次标记数组都要重新赋值
memset(res, -1, sizeof res);
for (int i = 0; i < l; i++) {
for (int j = 0; j < r; j++) {
string s;
cin >> s;
for (int k = 0; k < c; k++) {
// 这是我个人习惯,在图的问题中,把坐标都从1开始
g[i + 1][j + 1][k + 1] = s[k];
if (s[k] == 'S') {
st = {i + 1, j + 1, k + 1};
} else if (s[k] == 'E') {
ed = {i + 1, j + 1, k + 1};
}
}
}
}
bfs();
// 太长了,用 ans 保存着
int ans = res[ed.z][ed.x][ed.y];
if(ans != -1) printf("Escaped in %d minute(s).\n", ans);
else printf("Trapped!\n");
}
return 0;
}
完全二叉树的权值
问题描述:
给定一颗包含N个结点的完全二叉树,每个结点都有一个权值,命名规律如图所示:
现在小明要把相同深度的结点权值加在一起,他想知道哪个深度的结点权值之和最大。如果有多个深度的权值和最大,请你输出其中最小的深度值。根的深度设为1。
输入和输出格式:
输入:
第一行包括一个整数N,表示有N个结点
第二行包含N个整数,代表 A 1 , A 2 , A 3 . . . A n
输出:
一个整数,代表权值之和最大的深度值
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int a[N], n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
ll depth = 0, maxn = -99999999;
ll k = 0;
for (int i = 1; i <= n; i = i * 2)
{
ll sum = 0;
k++;
for (int j = i; j <= 2 * i - 1; j++)
{
sum += a[j];
}
if (sum > maxn)
{
maxn = sum;
depth = k;
}
}
cout << depth << endl;
return 0;
}