解析
什么破题
数据范围来看很明显最多到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可见我究竟调了多久)