zoukankan      html  css  js  c++  java
  • 【考试总结】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) 还水

  • 相关阅读:
    stream流
    python笔记:str.contains小坑之 UserWarning To actually get the groups, use str.extract.
    获取excel的行索引值
    Pandas学习(4、数据载入、存储及文件格式
    python中如何压缩和解压缩文件
    保持服务器屏幕可控制状态
    python判断文件和文件夹是否存在、创建文件夹
    PDF处理
    selenium+python配置chrome浏览器的选项
    python实现复制粘贴
  • 原文地址:https://www.cnblogs.com/yspm/p/14416962.html
Copyright © 2011-2022 走看看