【考试总结】2021-02-19 继续

倚天剑的愤怒

设考虑到 (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) 还水

原文地址:https://www.cnblogs.com/yspm/p/14416962.html