洛谷传送门
解析
什么破题
数据范围来看很明显最多到nlogn
首先,对于样例进行一下分析:
我们可以把它转化为一棵树:
每一个根都有对应的权,给你一个结点和加权和,问能爬到哪里
既然是树,自然要找爸爸(滑稽)
可以使用一个priority_queue来找:
for(int i=1;i<=n;i++){scanf("%d%d",&p[i].d,&p[i].c);p[i].id=i;while(!q.empty()){node m=q.top();if(m.d<p[i].d){//比当前这个小的//都出来认爸爸q.pop();p[m.id].father=i;}else break;}q.push((node){p[i].d,p[i].c,0,0,i});}
当然,最后还在队列中的就会无爸可归
孤苦伶仃不可怕,水池是我们共同的家
while(!q.empty()){node m=q.top();q.pop();p[m.id].father=0;}
既然是树,我们想到用倍增算法
用dp[i][j]表示从第i个盘接满(是指水真正经过)2^j个盘后所需的总水量
那么递推式就是:
。。。推不下去了
要想让这题得到递推式,我们需要得到dp[i][j-1]后水接到哪一个盘子了
那么就出现了:
dp结构体
(我都不敢写)
struct node2{int num,id;
}dp[100500][30];
//dp[i][j]num表示从i往下走
//2^j个有效的盘至少存的总水量,id表示到达的盘
那么递推式就是(再来一遍 ):
dp[i][j].num=dp[i][j-1].num +
dp[p[dp[i][j-1].id].father][j-1].num;//因为id
//表示j-1操作后接满的盘,所以得从它的爸爸开始
dp[i][j].id=dp[p[dp[i][j-1].id].father][j-1].id;
关于初始化,显然:
dp[i][0].num=p[i].c;//2^0个就是把自己接满了
dp[i][0].id=i;
然后就是写个倍增啦
scanf("%d%d",&r,&tot);int s=0,res,place=r,dep=p[r].deep;res=qu[p[r].deep];int flag=1;while(res>=0){//printf("res:%d\n",res);if(dep>=mi[res]&&dp[place][res].num + s<tot){s+=dp[place][res].num;place=p[dp[place][res].id].father;dep-=mi[res];}if(dp[place][res].num + s == tot){printf("%d\n",dp[place][res].id);flag=0;break;}res --;}if(flag) printf("%d\n",place);
最后一块拼图也已补齐,让我们
召唤神龙!!
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<string>
#include<queue>
#include<vector>
using namespace std;
int n,x;
long long sum[100500]={ };
struct node{int d,c,father,deep,id;bool operator < (const node& oth) const{return d > oth.d;}
}p[100500];
struct node2{int num,id;
}dp[100500][30];
//dp[i][j]num表示从i往下走2^j个有效的盘至少存的总水量,id表示到达的盘
priority_queue<node>q;
int mi[20];
int qu[100500];
void solve(){mi[0]=1;for(int i=1;i<=18;i++){mi[i]=mi[i-1] * 2;}int k=1;for(int i=1;i<=n;i++){if(mi[k]<=i) k++;qu[i]=k-1;}
}
int main(){int flag=1;scanf("%d%d",&n,&x);solve();for(int i=1;i<=n;i++){scanf("%d%d",&p[i].d,&p[i].c);p[i].id=i;while(!q.empty()){node m=q.top();if(m.d<p[i].d){q.pop();p[m.id].father=i;}else break;}q.push((node){p[i].d,p[i].c,0,0,i});}while(!q.empty()){node m=q.top();q.pop();p[m.id].father=0;}for(int i=n;i>=1;i--){if(p[i].father==0) p[i].deep=1;else p[i].deep=p[p[i].father].deep+1;}int k=qu[n];//printf("---------\n");for(int i=1;i<=n;i++){dp[i][0].num=p[i].c;dp[i][0].id=i;//printf("%d:%d\n",i,p[i].father);}//printf("---------\n");for(int j=1;j<=k;j++){for(int i=1;i<=n;i++){if(j>qu[p[i].deep]) continue;dp[i][j].num=dp[i][j-1].num +dp[p[dp[i][j-1].id].father][j-1].num;dp[i][j].id=dp[p[dp[i][j-1].id].father][j-1].id;//printf("%d %d: %d %d %d\n",i,j,dp[i][j].num,//dp[i][j-1].num,dp[dp[i][j-1].id][j-1].num); }}int r,tot;/*printf("---------\n");for(int i=1;i<=n;i++){printf("%d : %d %d %d\n",i,p[i].deep,qu[p[i].deep],dp[i][qu[p[i].deep]].num);printf("\n");}printf("---------\n");*/for(int k=1;k<=x;k++){scanf("%d%d",&r,&tot);int s=0,res,place=r,dep=p[r].deep;res=qu[p[r].deep];int flag=1;while(res>=0){//printf("res:%d\n",res);if(dep>=mi[res]&&dp[place][res].num + s<tot){s+=dp[place][res].num;place=p[dp[place][res].id].father;dep-=mi[res];//printf("%d : %d %d %d\n",r,place,s,res);}if(dp[place][res].num + s == tot){printf("%d\n",dp[place][res].id);flag=0;break;}res --;}if(flag) printf("%d\n",place);}return 0;
}
(无处不在的printf可见我究竟调了多久)