倚天剑的愤怒
设考虑到 (i) 后得到的数是 (x),如果 (x+v_i<0) 则后面连续的负数都要删掉,连续正的肯定都不删
这样配合线段树和二分可以做到 (Theta(mnlog^2n)),如果离线可能会有更好的复杂度
这个做法会被正负区间交错的数据卡掉,然而发现每个正数会拿来消掉一些负数
直接对着正数二分加线段树维护前缀最小值是不对的,这个正数抵消的时候可以抵消后面的负数来达到最小的抛弃,所以这部分堆扫一遍就行
把取堆里面每个元素的前缀和代价搞出来,离线扫一下
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Down(i,a,b) for(register int i=a;i>=b;i--)
#define reg register
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void swap(int &x,int &y){int t=x; x=y; y=t; return ;}
namespace yspm{
inline int read(){
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=1e6+10;
int a[N],n,ans[N],m,rem,num[N],need[N];
priority_queue<int> q;
struct node{int v,id;}ask[N];
inline bool cmp(node a,node b){return a.v<b.v;}
signed main(){
n=read(); m=read(); for(reg int i=1;i<=n;++i) a[i]=read();
for(reg int i=n;i>=1;--i){
if(a[i]<0) q.push(a[i]);
else if(a[i]>0){
while(q.size()){
if(q.top()+a[i]>=0) a[i]+=q.top(),q.pop();
else break;
} if(q.size()){
int tmp=a[i]+q.top(); q.pop();
q.push(tmp);
}
}
}
int cnt=0; num[++cnt]=q.size(); need[cnt]=0;
while(q.size()) ++cnt,num[cnt]=num[cnt-1]-1,need[cnt]=need[cnt-1]-q.top(),q.pop();
for(reg int i=1;i<=m;++i) ask[i].v=read(),ask[i].id=i;
sort(ask+1,ask+m+1,cmp);
int now=1; num[cnt+1]=-1;
for(reg int i=1;i<=m;++i){
while(now<=cnt&&ask[i].v-need[now]>=0) ++now; ans[ask[i].id]=num[now]+1;
}
for(reg int i=1;i<=m;++i) printf("%lld
",ans[i]);
return 0;
}
}
signed main(){return yspm::main();}
原谅
按权值排序之后逆序扫,把当前所有点都加入的是需要合并它和它们的 (lca) 的链
那么如果加入某个点后存下来的最小值和这个值一样,更新答案
如果有一个权值全部加入之后最小值满足大于等于次于其的第一个,枚举对应的在连通块周边的点扩展
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Down(i,a,b) for(register int i=a;i>=b;i--)
#define reg register
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void swap(int &x,int &y){int t=x; x=y; y=t; return ;}
namespace yspm{
inline int read(){
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=1e6+10;
int head[N],cnt,top[N],fa[N],dep[N],son[N],dfn[N],tim,sz[N];
struct node{int to,nxt;}e[N<<1];
inline void add(int u,int v){
e[++cnt].to=v; e[cnt].nxt=head[u];
return head[u]=cnt,void();
}
inline void dfs1(int x,int fat){ sz[x]=1; fa[x]=fat; dep[x]=dep[fat]+1;
for(reg int i=head[x];i;i=e[i].nxt){
int t=e[i].to; if(t==fat) continue;
dfs1(t,x); sz[x]+=sz[t]; if(sz[son[x]]<sz[t]) son[x]=t;
} return ;
}
inline void dfs2(int x,int topf){
top[x]=topf; dfn[x]=++tim; if(!son[x]) return ; dfs2(son[x],topf);
for(reg int i=head[x];i;i=e[i].nxt){
int t=e[i].to; if(t==fa[x]||t==son[x]) continue;
dfs2(t,x);
} return ;
}
inline int get(int x,int y){
while(top[x]!=top[y]) if(dep[top[x]]>dep[top[y]]) x=fa[top[x]]; else y=fa[top[y]];
if(dep[x]>dep[y]) swap(x,y); return x;
}
struct point{
int v,id;
bool operator <(const point &a)const{return v>a.v;}
}a[N];
bool vis[N];
int ans,mn=1e18+10,v[N],num,now,n,k;
inline void merge(int x){if(vis[x]) return ; num++; vis[x]=1; mn=min(mn,v[x]); return ;}
inline void cover(int x,int anc){
while(x!=anc&&!vis[x]) merge(x),x=fa[x];
return ;
}
inline void dfs(int x,int val){
if(vis[x]||num>=k) return ; merge(x); for(reg int i=head[x];i;i=e[i].nxt){
if(v[e[i].to]==val) dfs(e[i].to,val);
}return ;
}
signed main(){
freopen("1.in","r",stdin);
n=read(); k=read();
for(reg int i=1;i<=n;++i) v[i]=a[i].v=read(),a[i].id=i; sort(a+1,a+n+1);
for(reg int i=1,u,v;i<n;++i) u=read()+1,v=read()+1,add(u,v),add(v,u);
dfs1(1,0); dfs2(1,1);
now=a[1].id; ans=1; merge(a[1].id); vis[0]=1;
for(reg int i=2,las,r;num<=k&&i<=n;i=r+1){
r=i+1; while(a[r].v==a[i].v&&r<=n) ++r; --r;
for(reg int j=i;j<=r;++j){
now=get(las=now,a[j].id);
merge(now); cover(a[j].id,now);
if(las!=now) cover(fa[las],now);
if(mn==a[j].v&&num<=k) ans=num;
} if(num>k) break;
// cout<<k<<endl;
// for(reg int i=1;i<=n;++i) if(vis[i]) cout<<i<<" "; puts("");
// cout<<mn<<endl;
if(mn>=a[r+1].v){
int tv=a[r+1].v,rnxt=r+1; while(rnxt<=n&&a[rnxt].v==tv) rnxt++; --rnxt;
if(rnxt>n) continue;
for(reg int j=r+1;j<=rnxt;++j){
if(vis[a[j].id]) continue;
for(reg int ed=head[a[j].id];ed;ed=e[ed].nxt){
if(vis[e[ed].to]) dfs(a[j].id,a[j].v);
} if(num>k) break;
} ans=num;
}
} printf("%lld
",ans);
return 0;
}
}
signed main(){return yspm::main();}
收集
没写的都是简单题/dk
发现这个带修就是 ( exttt{SDOI2015寻宝游戏})
做法是用 (set) 维护 (dfn) 序:所有点按照 (dfn) 序插入 (set),新增和减少都对两边的点加减距离
而这个和上题不同的是这题并不需要返回,也就是如果把所有的关键点提出来是 ( exttt{ZJOI2007捉迷藏})
直接线段树维护直径维护即可,由于是二合一,所以代码长但是无脑
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Down(i,a,b) for(register int i=a;i>=b;i--)
#define reg register
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void swap(int &x,int &y){int t=x; x=y; y=t; return ;}
namespace yspm{
inline int read(){
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=1e5+10;
struct edge{int to,nxt,dis;}e[N<<1];
int head[N],cnt,dep[N],son[N],dfn[N],ord[N],tim,top[N],dis[N],fa[N],sz[N];
inline void add(int u,int v,int w){
e[++cnt].to=v; e[cnt].nxt=head[u]; e[cnt].dis=w;
return head[u]=cnt,void();
}
inline void dfs1(int x,int y){
dep[x]=dep[y]+1; fa[x]=y; sz[x]=1;
for(reg int i=head[x];i;i=e[i].nxt){
int t=e[i].to; if(t==y) continue;
dis[t]=dis[x]+e[i].dis; dfs1(t,x); sz[x]+=sz[t]; if(sz[t]>sz[son[x]]) son[x]=t;
} return ;
}
inline void dfs2(int x,int topf){
dfn[x]=++tim; top[x]=topf; ord[tim]=x; if(son[x]) dfs2(son[x],topf);
for(reg int i=head[x];i;i=e[i].nxt){
int t=e[i].to; if(t==fa[x]||t==son[x]) continue;
dfs2(t,t);
}return ;
}
inline int lca(int x,int y){
while(top[x]!=top[y]) if(dep[top[x]]<dep[top[y]]) y=fa[top[y]]; else x=fa[top[x]];
return dep[x]<dep[y]?x:y;
}
inline int dist(int x,int y){return x&&y?dis[x]+dis[y]-dis[lca(x,y)]*2:0;}
struct node{
int p1,p2,dis;
node(){}
node(int xx,int yy,int zz){p1=xx; p2=yy; dis=zz; return ;}
bool operator<(const node &a)const{return dis<a.dis;}
}t[N<<2];
inline node merge(node a,node b){
int tans;
if(a.dis!=-1){
if(b.dis!=-1){
node ans=a.dis<b.dis?b:a;
if(ans.dis<(tans=dist(a.p1,b.p1))) ans=node(a.p1,b.p1,tans);
if(ans.dis<(tans=dist(a.p2,b.p2))) ans=node(a.p2,b.p2,tans);
if(ans.dis<(tans=dist(a.p1,b.p2))) ans=node(a.p1,b.p2,tans);
if(ans.dis<(tans=dist(a.p2,b.p1))) ans=node(a.p2,b.p1,tans);
return ans;
}
return a;
}else{
if(b.dis!=-1) return b;
return node(-1,-1,-1);
}
}
inline void upd(int p,int l,int r,int pos){
if(l==r){
if(!t[p].p1) t[p].p1=ord[l],t[p].p2=ord[l],t[p].dis=0;
else t[p].p1=0,t[p].p2=0,t[p].dis=-1;
return ;
}int mid=(l+r)>>1;
if(pos<=mid) upd(p<<1,l,mid,pos); else upd(p<<1|1,mid+1,r,pos);
return t[p]=merge(t[p<<1],t[p<<1|1]),void();
}
inline void build(int p,int l,int r){
if(l==r) return t[p].dis=-1,void();
build(p<<1,l,(l+r)>>1); build(p<<1|1,((l+r)>>1)+1,r);
t[p].dis=-1; return ;
}
int n,Q,ans,x,l,r;
bool vis[N];
set<int>st;
signed main(){
freopen("1.in","r",stdin);
n=read(); Q=read(); for(int u,v,w,i=1;i<n;++i) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
dfs1(1,0); dfs2(1,1);
build(1,1,n);
while(Q--){
x=read(); upd(1,1,n,dfn[x]); vis[x]^=1; if(!vis[x]) st.erase(dfn[x]);
set<int>::iterator it=st.lower_bound(dfn[x]);
if(st.empty()) l=r=0; else if(it==st.begin()||it==st.end()) l=*st.begin(),r=*--st.end();
else l=(*it),r=(*--it);
if(vis[x]) st.insert(dfn[x]),ans+=dist(x,ord[l])+dist(x,ord[r])-dist(ord[l],ord[r]);
else ans-=dist(x,ord[l])+dist(x,ord[r])-dist(ord[l],ord[r]);
printf("%lld
",ans-(t[1].dis==-1?0:t[1].dis));
}
return 0;
}
}
signed main(){return yspm::main();}
这两天的 (CF) 和考试都出现了正解想到但是没有调完的问题,少了一整个题的分数
今天吃的另外一个教训就是没有看最后一题,太遗憾了,二合一比 (T_1) 还水