题解:
类似烽火传递,设定状态:\(f[i]\)为讨论到第i号点的最小损失,则转移方程为:\(f[i]=min{f[j]}+a[i] (i-k-1<=j<=i-1)\),时间复杂度:\(O(nk)\)可使用单调队列维护最小值,本次使用\(zkw\)维护区间最小
\(code\):
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<ctype.h>
#include<vector>
#define ll long long
#define inf 2312312321321312LL
#define l(x) x<<1
#define r(x) x<<1|1
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc() {
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
void read(T &x) {
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(x=0,flag=1):(x=tt-'0');
while(isdigit(tt=gc())) x=x*10+tt-'0';
if(flag) x=-x;
}
ll n,k,sum,ans=inf,M=1;
ll a[100005],f[100005],c[100005*40];
void modify(ll x,ll d) {
x+=M;
c[x]=min(c[x],d);
while(x>1) {
x>>=1;
c[x]=min(c[l(x)],c[r(x)]);
}
}
ll query(ll x,ll y) {
ll minn=inf;
for(x+=M-1,y+=M+1; x^y^1; x>>=1,y>>=1) {
if(~x&1) minn=min(minn,c[x^1]);
if(y&1) minn=min(minn,c[y^1]);
}
return minn;
}
int main() {
freopen("grass.in","r",stdin);
freopen("grass.out","w",stdout);
read(n),read(k);
while(M<=n+1) M<<=1;
for(int i=2; i<=n+1; i++) read(a[i]),sum+=a[i],f[i]=inf;
memset(c,127,sizeof(c));
f[1]=0;
modify(1,0);
for(int i=2; i<=n+1; i++) {
ll minn=query(max(i-k-1,1LL),i-1);
f[i]=min(f[i],minn+a[i]);
modify(i,f[i]);
}
printf("%lld",sum-query(n-k+1,n+1));
}
题解:
用大根堆维护\(in\),\(out\),分别表示需买入和需送出,存储:\(i*z+v_j\),保证每次可以对前面的选择“后悔”
\(code:\)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<ctype.h>
#include<vector>
#define ll long long
#define inf 2312312321321312LL
#define l(x) x<<1
#define r(x) x<<1|1
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc() {
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
void read(T &x) {
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(x=0,flag=1):(x=tt-'0');
while(isdigit(tt=gc())) x=x*10+tt-'0';
if(flag) x=-x;
}
priority_queue<ll>in_,out_;
ll v,n,x,y,z,ans;
int main() {
read(n),read(x),read(y),read(z);
for(int i=1; i<=n; i++) {
ll a,b;
read(a),read(b);
if(a-b>0) {
for(int k=1; k<=a-b; k++) {
if(in_.empty())
ans+=y,out_.push(i*z+y);
else {
v=-in_.top();
if(i*z+v<y)
ans+=i*z+v,in_.pop(),out_.push(i*z*2+v);
else
ans+=y,out_.push(i*z+y);
}
}
} else {
for(int k=1; k<=b-a; k++) {
if(out_.empty())
ans+=x,in_.push(i*z+x);
else {
v=-out_.top();
if(i*z+v<x)
ans+=i*z+v,out_.pop(),in_.push(i*z*2+v);
else
ans+=x,in_.push(i*z+x);
}
}
}
}
printf("%lld",ans);
}
题解:
\(dfs\)序后分块维护块内有序,查询时二分查找块内,块两端暴力查找即可
\(code:\)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<math.h>
#include<ctype.h>
#include<vector>
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
inline char gc() {
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?0:*p1++;
}
template<typename T>
void read(T &x) {
char tt;
bool flag=0;
while(!isdigit(tt=gc())&&tt!='-');
tt=='-'?(x=0,flag=1):(x=tt-'0');
while(isdigit(tt=gc())) x=x*10+tt-'0';
if(flag) x=-x;
}
ll n,tmp[100005],a[100005],s,block[320][320];
vector<ll>G[100005];
ll fa[100005],pos[100005][2],tot;
void dfs(ll x,ll pre) {
fa[x]=pre;
pos[x][0]=++tot;
int siz=G[x].size();
for(int i=0; i<siz; i++) {
ll p=G[x][i];
if(p==pre) continue;
dfs(p,x);
}
pos[x][1]=tot;
}
ll query(ll x,ll y,ll d) {
ll lid=(x-1)/s+1;
ll rid=(y-1)/s+1;
ll tot=0;
if(lid==rid) {
for(int i=x; i<=y; i++)
if(a[i]>d) tot++;
} else {
for(int i=x; i<=lid*s; i++)
if(a[i]>d) tot++;
for(int i=(rid-1)*s+1; i<=y; i++)
if(a[i]>d) tot++;
for(int i=lid+1; i<rid; i++) {
int id=upper_bound(block[i]+1,block[i]+1+s,d)-block[i];
tot+=s-id+1;
}
}
return tot;
}
int main() {
read(n);
for(int i=1; i<=n; i++)
read(tmp[i]);
for(int i=2; i<=n; i++) {
ll x;
read(x);
G[x].push_back(i);
}
dfs(1,1);
for(int i=1; i<=n; i++) a[pos[i][0]]=tmp[i];
s=sqrt(n);
for(int i=1; i<=n; i++)
block[(i-1)/s+1][(i-1)%s+1]=a[i];
for(int i=1; i<=s+1; i++) sort(block[i]+1,block[i]+1+s);
for(int i=1; i<=n; i++)
printf("%lld\n",query(pos[i][0],pos[i][1],a[pos[i][0]]));
}