test201909027 老Z

30+100+40=170。数据出锅*2,也是没谁了。

装饰

快要到 Mope 的生日了,Mope 希望举行一场盛大的生日派对。

派对的准备工作中当然有装饰房间啦。Mope 的房间里有按从左到右的顺序排好的 n 个装饰品,其中第 i 个装饰品被挂在高为 ai 的地方。Mope 不希望改变它们的位置,所以他决定取下一些饰品,设留下来的序列为{bi},他希望任意相邻三项 bi, bi+1, bi+2 满足bi ≤ bi+1 ≤ bi+2或者bi ≥ bi+1 ≥ bi+2。Mope 希望知道他至少要取下多少个饰品。

对于所有测试点:1 ≤ n ≤ 200,000, 1 ≤ ai ≤ 1,000,000,000

题解

维护三种 DP 数组,fi 表示以高度 i 为结尾的最后两项是严格上升的最长子序列长度,gi 表示以高度 i 为结尾的最后两项是严格下降的最长子序列长度,hi 表示以高度 i 为结尾的最后两项是严格相等的最长子序列长度。

转移十分显然,线段树维护即可。时间复杂度 O(n log n)。

co int N=200000+10;
int a[N],b[N];

int f[N*4],g[N*4],h[N*4];
#define lc (x<<1)
#define rc (x<<1|1)

void chgf(int x,int l,int r,int p,int v){
	if(l==r){
		f[x]=max(f[x],v);
		return;
	}
	int mid=(l+r)>>1;
	if(p<=mid) chgf(lc,l,mid,p,v);
	else chgf(rc,mid+1,r,p,v);
	f[x]=max(f[lc],f[rc]);
}
void chgg(int x,int l,int r,int p,int v){
	if(l==r){
		g[x]=max(g[x],v);
		return;
	}
	int mid=(l+r)>>1;
	if(p<=mid) chgg(lc,l,mid,p,v);
	else chgg(rc,mid+1,r,p,v);
	g[x]=max(g[lc],g[rc]);
}
void chgh(int x,int l,int r,int p,int v){
	if(l==r){
		h[x]=max(h[x],v);
		return;
	}
	int mid=(l+r)>>1;
	if(p<=mid) chgh(lc,l,mid,p,v);
	else chgh(rc,mid+1,r,p,v);
	h[x]=max(h[lc],h[rc]);
}

int qryf(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return f[x];
	int mid=(l+r)>>1;
	if(qr<=mid) return qryf(lc,l,mid,ql,qr);
	if(ql>mid) return qryf(rc,mid+1,r,ql,qr);
	return max(qryf(lc,l,mid,ql,qr),qryf(rc,mid+1,r,ql,qr));
}
int qryg(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return g[x];
	int mid=(l+r)>>1;
	if(qr<=mid) return qryg(lc,l,mid,ql,qr);
	if(ql>mid) return qryg(rc,mid+1,r,ql,qr);
	return max(qryg(lc,l,mid,ql,qr),qryg(rc,mid+1,r,ql,qr));
}
int qryh(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return h[x];
	int mid=(l+r)>>1;
	if(qr<=mid) return qryh(lc,l,mid,ql,qr);
	if(ql>mid) return qryh(rc,mid+1,r,ql,qr);
	return max(qryh(lc,l,mid,ql,qr),qryh(rc,mid+1,r,ql,qr));
}

int main(){
	freopen("decoration.in","r",stdin),freopen("decoration.out","w",stdout);
	int n=read<int>();
	for(int i=1;i<=n;++i) b[i]=read(a[i]);
	sort(b+1,b+n+1);
	int m=unique(b+1,b+n+1)-b-1;
	
	for(int i=1;i<=n;++i){
//		cerr<<"calc "<<i<<endl;
		a[i]=lower_bound(b+1,b+m+1,a[i])-b;
		
		int tmpf=a[i]>1?max(qryf(1,1,m,1,a[i]-1),qryh(1,1,m,1,a[i]-1)):0;
		int tmpg=a[i]<m?max(qryg(1,1,m,a[i]+1,m),qryh(1,1,m,a[i]+1,m)):0;
		int tmph=max(qryf(1,1,m,a[i],a[i]),max(qryg(1,1,m,a[i],a[i]),qryh(1,1,m,a[i],a[i])));
//		cerr<<" f="<<tmpf<<" g="<<tmpg<<" h="<<tmph<<endl;
		
		chgf(1,1,m,a[i],tmpf+1);
		chgg(1,1,m,a[i],tmpg+1);
		chgh(1,1,m,a[i],tmph+1);
	}
	int ans=max(qryf(1,1,m,1,m),max(qryg(1,1,m,1,m),qryh(1,1,m,1,m)));
//	cerr<<"ans="<<ans<<endl;
	printf("%d\n",n-ans);
	return 0;
}

大样例是错的导致我调了一个半小时,后来写了个对拍才逐渐怀疑std出锅。

最短路径

Mope 做了一个梦。

在梦中,Mope 在一座沼泽地里。沼泽地里有 n 个村落,由 m 座木桥连接并连通。他发现自己的手臂上出现了一个数字,这个数字一开始是 0。每一座木桥上也有一个数字 wi。当 Mope 经过第 i 座桥时,他手臂上的数字会变成和 wi做按位或的结果。Mope 现在在 1 号村落,村落里的长老告诉他在 n 号村落里有一个魔法宝箱,他手臂上的数字越大,宝箱里的财宝就越少。Mope 希望知道当他到达 n 号村落时,手臂上的数字最小能是多少。

对于所有测试点:1 ≤ n ≤ 100,000, n − 1 ≤ m ≤ min(n(n−1)/2 , 200,000), 0 ≤wi < 230

题解

按位贪心,并查集判连通性即可。时间复杂度 O(n log w)。

co int N=100000+10;
struct edge {int u,v,w;}e[2*N];
int fa[N];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}

int main(){
	freopen("path.in","r",stdin),freopen("path.out","w",stdout);
	int n=read<int>(),m=read<int>();
	for(int i=1;i<=m;++i) read(e[i].u),read(e[i].v),read(e[i].w);
	int ans=0;
	for(int c=29;c>=0;--c){
		for(int i=1;i<=n;++i) fa[i]=i;
		for(int i=1;i<=m;++i)if((e[i].w>>c|ans>>c)==ans>>c){
			int fu=find(e[i].u),fv=find(e[i].v);
			if(fu!=fv) fa[fu]=fv;
		}
		if(find(1)!=find(n)) ans^=1<<c;
	}
	printf("%d\n",ans);
	return 0;
}

繁衍

2077 年,生物研究者们发现了一种神奇的生物——Mebius。

Mebius 是一种单细胞生物,生殖方式为分裂生殖。目前研究者们已发现了Mebius 的 n 个亚种,分别标号为 1~n。奇特的是,某个亚种的 Mebius 可能分裂成其他亚种的 Mebius。

Mope 是一名生物研究员,他发现 1 类亚种 Mebius 潜藏着巨大的医学价值。为了一探究竟,他需要大量的 1 类亚种 Mebius。他现在拥有一些不同种类的Mebius,他拥有大量的某种分裂促进因子可以使 Mebius 立即分裂。我们可以认为 Mope 拥有几乎无限量的分裂促进因子。Mope 想知道他最多能得到多少 1 类Mebius。

对于所有测试点:1 ≤ n ≤ 100,000, ∑ki ≤ 500,000, 0 ≤ ci ≤ 109

题解

先去掉到不了 1 的点,然后就考虑一下环的情况就行了。

容易发现若 ci>0,那么只能存在一个包含 1 的简单环。若有 ci=0,那么情况要复杂些,用 Tarjan 将 SCC 缩点后原图变成 DAG,那么对于一个环如果能到它的点和它自己的点的点权和是 0 的话它也是合法的。

这些都可以用 Tarjan 和拓扑排序判断。时间复杂度 O(n+m)。

co int N=100000+10;
int cnt[N];
vector<int> to[N],re[N];
int vis[N];

vector<int> scc[N];
int bl[N],tot;
int pos[N],low[N],dfn,st[N],top,ins[N];

void tarjan(int x,int fa){
	pos[x]=low[x]=++dfn;
	st[++top]=x,ins[x]=1;
	for(int i=0;i<(int)re[x].size();++i){
		int y=re[x][i];
		if(!pos[y]){
			tarjan(y,x);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y]) low[x]=min(low[x],pos[y]);
	}
	if(low[x]==pos[x]){
		++tot;
		do{
			int y=st[top];
			ins[y]=0;
			bl[y]=tot,scc[tot].push_back(y);
		}while(st[top--]!=x);
	}
}

LL scnt[N];
vector<int> sto[N];
int svis[N];

void build(int x){
	svis[x]=1;
	for(int i=0;i<(int)sto[x].size();++i){
		int y=sto[x][i];
		if(svis[y]){
			scnt[x]+=scnt[y]; // edit 1
			continue;
		}
		build(y);
		scnt[x]+=scnt[y];
	}
	if(scnt[x])for(int i=0;i<(int)scc[x].size();++i) vis[scc[x][i]]=1;
}

int deg[N];
deque<int> Q;

int cir[N],len;
void dfs(int x){
	vis[x]=2,cir[++len]=x;
	for(int i=0;i<(int)to[x].size();++i){
		int y=to[x][i];
		if(vis[y]==1) dfs(y);
	}
}
int main(){
	freopen("multiplication.in","r",stdin),freopen("multiplication.out","w",stdout);
	read<int>();
	int n=read<int>();
	for(int x=1;x<=n;++x){
		for(int k=read<int>();k--;){
			int y=read<int>();
			to[x].push_back(y),re[y].push_back(x);
		}
	}
	for(int i=1;i<=n;++i) read(cnt[i]);	
	
	tarjan(1,0);
	
	for(int i=1;i<=tot;++i)
		for(int j=0;j<(int)scc[i].size();++j){
			int x=scc[i][j];
			scnt[i]+=cnt[x];
			for(int k=0;k<(int)re[x].size();++k){
				int y=re[x][k];
				if(i!=bl[y]) sto[i].push_back(bl[y]);
			}
		}
	build(bl[1]);
	
	for(int x=1;x<=n;++x)if(vis[x])
		for(int i=0;i<(int)to[x].size();++i){
			int y=to[x][i];
			if(vis[y]) ++deg[y];
		}
	for(int x=1;x<=n;++x)if(vis[x] and !deg[x]) Q.push_back(x);
	while(Q.size()){
		int x=Q.front();
		vis[x]=2,Q.pop_front();
		for(int i=0;i<(int)to[x].size();++i){
			int y=to[x][i];
			if(vis[y]){
				cnt[y]=add(cnt[y],cnt[x]);
				if(--deg[y]==0) Q.push_back(y);
			}
		}
	}
	
	int valid_deg=1,cir_siz=0;
	for(int i=1;i<=n;++i)if(vis[i]==1){
		if(deg[i]>1) {valid_deg=0;break;}
		++cir_siz;
	}
	if(cir_siz) dfs(1);
	if(!valid_deg or cir_siz!=len) {puts("Infinity");return 0;}
	
	int ans=0;
	if(cir_siz)for(int i=1;i<=len;++i) ans=add(ans,cnt[cir[i]]);
	else ans=cnt[1];
	printf("%d\n",ans);
	return 0;
}

这玩意真不适合断断续续写代码,一个小错调一下午。

posted on 2019-09-27 19:13  autoint  阅读(146)  评论(0编辑  收藏  举报

导航