省选测试24

A. 倚天剑的愤怒

分析

\(15\) 分的做法是从前往后扫,遇到不合法的时候就从前面找一个最小的减掉

正解需要倒着考虑

维护一个 \(multiset\)

如果遇到一个负数直接扔到 \(multiset\)

如果遇到一个正数,那么我们肯定想让它抵消掉尽可能多的负数,直到它变成零

最后把剩下的负数做一个前缀和,二分查找即可

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<set>
#define rg register
const int maxn=1e6+5;
int n,m,a[maxn],cnt,tp;
long long beg,sum[maxn];
std::multiset<int> s;
#define sit std::multiset<int>::iterator
int main(){
	scanf("%d%d",&n,&m);
	for(rg int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(rg int i=n;i>=1;i--){
        if(a[i]<0) s.insert(-a[i]);
        else {
            while(!s.empty()){
                rg int tmp=*s.begin();
                s.erase(s.begin());
                if(a[i]>=tmp) a[i]-=tmp;
                else {
                    s.insert(tmp-a[i]);
                    break;
                }
            }
        }
    }
    for(rg sit it=s.begin();it!=s.end();++it) sum[++tp]=*it;
    for(rg int i=1;i<=tp;i++) sum[i]+=sum[i-1];
	for(rg int i=1;i<=m;i++){
		scanf("%lld",&beg);
        if(beg>=sum[tp]) cnt=0;
        else cnt=tp-(std::upper_bound(sum+1,sum+1+tp,beg)-sum)+1;
		printf("%d\n",cnt);
	}
	return 0;
}

B. 原谅

分析

考试的时候挂成了 \(40\)

调了一下午+一晚上,拍了几千组没拍出错

然后去 \(51Nod\) 上下载数据,手调 \(10000\) 个节点的树

发现是删叶子节点的时候删挂了

一条边我强制让权值小的连向权值大的,然后没考虑权值相等的

这样在更新的时候就有可能更新不到

这道题的思路还是比较简单的

将点按照权值从大到小排序

对于当前一个值 \(x\)

先把权值大于等于 \(x\) 的加入并查集

然后删权值等于 \(x\) 的叶子节点,能删多少删多少

要求删完之后必须在一个联通块中并且联通块的大小小于等于 \(k\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<queue>
#include<vector>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int n,m;
struct asd{
	int zb,yb;
	asd(){}
	asd(rg int aa,rg int bb){
		zb=aa,yb=bb;
	}
}b[maxn];
int du[maxn],a[maxn],id[maxn],ans,fa[maxn],cnt,vis[maxn],tim,siz[maxn],maxsiz=1;
std::vector<int> g[maxn],g2[maxn];
bool cmp(rg int aa,rg int bb){
	return a[aa]>a[bb];
}
int sta[maxn],tp;
int zhao(rg int xx){
	if(xx==fa[xx]) return xx;
	return fa[xx]=zhao(fa[xx]);
}
void bing(rg int xx,rg int yy){
	xx=zhao(xx),yy=zhao(yy);
	if(xx==yy) return;
	fa[xx]=yy,siz[yy]+=siz[xx],siz[xx]=0;
	maxsiz=std::max(maxsiz,siz[yy]);
	cnt--;
}
std::queue<int> q;
void updat(rg int now){
	for(rg int j=0;j<g[now].size();j++){
		rg int u=g[now][j];
		du[u]++,du[now]++;
	}
	sta[++tp]=now;
}
int main(){
	n=read(),m=read();
	for(rg int i=1;i<=n;i++) a[i]=read(),fa[i]=i,siz[i]=1;
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		aa=read()+1,bb=read()+1;
		if(a[aa]>a[bb]) g[bb].push_back(aa);
		else g[aa].push_back(bb);
		if(a[aa]==a[bb]) g2[aa].push_back(bb),g2[bb].push_back(aa);
	}
	for(rg int i=1;i<=n;i++) id[i]=i;
	std::sort(id+1,id+1+n,cmp);
	rg int lat=1e9,tmp=0,haha=0;
	for(rg int i=1;i<=n+1;i++){
		rg int now=id[i];
		if(a[now]==lat){
			updat(now);
			continue;
		}
		tim++,tmp=0,haha=0;
		for(rg int j=1;j<=tp;j++){
			if(du[sta[j]]<=1) q.push(sta[j]),vis[sta[j]]=tim,tmp++;
		}
		while(!q.empty()){
			rg int tmp1=q.front();q.pop();
			for(rg int j=0;j<g2[tmp1].size();j++){
				rg int u=g2[tmp1][j];
				if(vis[u]!=tim){
					du[u]--;
					if(du[u]==1){
						vis[u]=tim,tmp++;
						q.push(u);
					}
				}
			}
		}
		for(rg int j=1;j<=tp;j++){
			if(vis[sta[j]]==tim) continue;
			for(rg int k=0;k<g[sta[j]].size();k++){
				rg int u=g[sta[j]][k];
				if(vis[u]==tim) continue;
				bing(sta[j],u);
			}
		}
		cnt+=tp-tmp;
		if(maxsiz>m) break;
		if(cnt<=1) ans=std::max(ans,maxsiz),haha=1;
		for(rg int j=1;j<=tp;j++){
			for(rg int k=0;k<g[sta[j]].size();k++){
				rg int u=g[sta[j]][k];
				bing(sta[j],u);
			}
		}
		cnt+=tmp;
		if(haha) ans=std::max(ans,std::min(m,siz[zhao(id[1])]));
		if(tim==2) ans=std::max(ans,std::min(maxsiz,m));
		if(ans==m) break;
		lat=a[now],tp=0;
		updat(now);
	}
	printf("%d\n",ans);
	return 0;
}

C. 收集

分析

暑假集训的时候做过一个类似的题,然而没想起来

有一个结论:\(DFS\) 序求出后,假设关键点按照 \(DFS\) 序排序后是 \(\{a_1,a_2,\ldots ,a_k\}\)

那么所有关键点形成的极小连通子树的边权和的两倍等于 \(\mathrm{dist}(a_1,a_2)+\mathrm{dist}(a_2,a_3)+\cdots+\mathrm{dist}(a_{k-1},a_k)+\mathrm{dist}(a_k,a_1)\)

所以遇到一个点之后查询它的 \(dfn\) 序的前驱和后继即可

这道题因为不需要返回出发点

所以答案就是极小连通子树的边权和减去联通块直径

直径拿线段树维护一下就行了

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<set>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5;
struct asd{
	int to,nxt,val;
}b[maxn<<1];
int h[maxn],tot=1,n,m;
void ad(rg int aa,rg int bb,rg int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
int dfn[maxn],dfnc,siz[maxn],son[maxn],dep[maxn],fa[maxn],tp[maxn],vis[maxn],rk[maxn];
long long dis[maxn];
void dfs1(rg int now,rg int lat){
	fa[now]=lat;
	dep[now]=dep[lat]+1;
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dis[u]=dis[now]+b[i].val;
		dfs1(u,now);
		siz[now]+=siz[u];
		if(son[now]==0 || siz[u]>siz[son[now]]) son[now]=u;
	}
}
void dfs2(rg int now,rg int top){
	tp[now]=top;
	dfn[now]=++dfnc;
	rk[dfnc]=now;
	if(son[now]) dfs2(son[now],top);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==fa[now] || u==son[now]) continue;
		dfs2(u,u);
	}
}
int getlca(rg int xx,rg int yy){
	while(tp[xx]!=tp[yy]){
		if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
		xx=fa[tp[xx]];
	}
	return dep[xx]<dep[yy]?xx:yy;
}
long long getdis(rg int xx,rg int yy){
	return dis[xx]+dis[yy]-2LL*dis[getlca(xx,yy)];
}
struct trr{
	int l,r,zb,yb;
	long long val;
}tr[maxn<<2];
void push_up(rg int da){
	if(tr[da<<1].zb==-1){
		tr[da].zb=tr[da<<1|1].zb,tr[da].yb=tr[da<<1|1].yb,tr[da].val=tr[da<<1|1].val;
	} else if(tr[da<<1|1].zb==-1){
		tr[da].zb=tr[da<<1].zb,tr[da].yb=tr[da<<1].yb,tr[da].val=tr[da<<1].val;
	} else {
		tr[da].zb=tr[da<<1].zb,tr[da].yb=tr[da<<1].yb,tr[da].val=tr[da<<1].val;
		rg long long tmp=tr[da<<1|1].val;
		if(tmp>tr[da].val) tr[da].zb=tr[da<<1|1].zb,tr[da].yb=tr[da<<1|1].yb,tr[da].val=tmp;
		tmp=getdis(tr[da<<1].zb,tr[da<<1|1].zb);
		if(tmp>tr[da].val) tr[da].zb=tr[da<<1].zb,tr[da].yb=tr[da<<1|1].zb,tr[da].val=tmp;
		tmp=getdis(tr[da<<1].zb,tr[da<<1|1].yb);
		if(tmp>tr[da].val) tr[da].zb=tr[da<<1].zb,tr[da].yb=tr[da<<1|1].yb,tr[da].val=tmp;
		tmp=getdis(tr[da<<1].yb,tr[da<<1|1].zb);
		if(tmp>tr[da].val) tr[da].zb=tr[da<<1].yb,tr[da].yb=tr[da<<1|1].zb,tr[da].val=tmp;
		tmp=getdis(tr[da<<1].yb,tr[da<<1|1].yb);
		if(tmp>tr[da].val) tr[da].zb=tr[da<<1].yb,tr[da].yb=tr[da<<1|1].yb,tr[da].val=tmp;
	}
}
void build(rg int da,rg int l,rg int r){
	tr[da].zb=tr[da].yb=-1,tr[da].l=l,tr[da].r=r;
	if(l==r) return;
	rg int mids=(l+r)>>1;
	build(da<<1,l,mids),build(da<<1|1,mids+1,r);
}
void xg(rg int da,rg int wz,rg int op){
	if(tr[da].l==tr[da].r){
		if(op==1) tr[da].zb=tr[da].yb=wz;
		else tr[da].zb=tr[da].yb=-1;
		return;
	}
	rg int mids=(tr[da].l+tr[da].r)>>1;
	if(wz<=mids) xg(da<<1,wz,op);
	else xg(da<<1|1,wz,op);
	push_up(da);
}
long long ans;
#define sit std::set<int>::iterator
std::set<int> s;
int main(){
	memset(h,-1,sizeof(h));
	n=read(),m=read();
	rg int aa,bb,cc;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read(),cc=read();
		ad(aa,bb,cc),ad(bb,aa,cc);
	}
	dfs1(1,0);
	dfs2(1,1);
	rg sit it1,it2;
	build(1,1,n);
	for(rg int i=1;i<=m;i++){
		aa=read();
		vis[aa]^=1;
		if(vis[aa]){
			xg(1,aa,1);
			s.insert(dfn[aa]);
			if(s.size()>1){
				it1=it2=s.upper_bound(dfn[aa]);
				if(it1==s.end()) it1=s.begin();
				ans+=getdis(rk[*it1],aa);
				--it2;
				if(it2==s.begin()){
					it2=--s.end();
				} else {
					--it2;
				}
				ans+=getdis(rk[*it2],aa);
				ans-=getdis(rk[*it1],rk[*it2]);
			}
		} else {
			xg(1,aa,-1);
			if(s.size()>1){
				it1=it2=s.upper_bound(dfn[aa]);
				if(it1==s.end()) it1=s.begin();
				ans-=getdis(rk[*it1],aa);
				--it2;
				if(it2==s.begin()){
					it2=--s.end();
				} else {
					--it2;
				}
				ans-=getdis(rk[*it2],aa);
				ans+=getdis(rk[*it1],rk[*it2]);
			}
			s.erase(dfn[aa]);
		}
		printf("%lld\n",ans-tr[1].val);
	}
	return 0;
}
posted @ 2021-02-19 21:46  liuchanglc  阅读(26)  评论(0编辑  收藏  举报