noip模拟17

\(\color{white}{\mathbb{霞光划破暗淡天际,月影彷徨,鸡鸣仿佛,冀之以继往开来,名之以:黎明}}\)

今天似乎取得了有史以来最好的成绩~

pic.PNG

前两名都 A 掉了 \(t3\),然鹅 \(t3\) 上去就被我弃了……

前两天部分分都写得比较顺利
\(t1\) 一眼以为 \(bitset\) 裸题,开了 \(6e4 * 6e4\) 直接自信提交,后来一算才发现空间炸了
但是不记得一位 \(bitset\) 是多大,以为和 \(bool\) 一样,只能开 \(10^4 * 10^4\),没敢打另外 20% 的部分分,暴挂20分

\(t2\) 网络流板子忘得差不多了,口胡了还几次才过了样例


A. 世界线

读懂题很快就能看出来是 \(bitset\),但是空间比较卡
首先,\(bitset\) 是每8位占用一个字节,一次运算复杂度为 \(n/32\)
那么其实前 \(60%\) 都是可以轻松水过去的
发现最后两个点的理论空间都比能开下的 \(bitset\) 大一倍,那么可以采用把所有点分成两部分的方式统计,\(bitset\) 只开一半,第一次统计所有点能到达的 \(\le n/2\) 的点的个数,再来一次统计能到达的 \(>n/2\) 的点的个数,直接相加即可

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e4+5;
const int maxm=1e6+5;
int n,m,deg[maxn],x,y,hd[maxn],cnt,limit,out[maxn];
bitset<maxn/2>vis[maxn];
long long ans;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();	
	}
	return x*f;
}
struct Edge{
	int nxt,to;	
}edge[maxm];
void add(int u,int v){
	edge[++cnt].nxt=hd[u];
	edge[cnt].to=v;
	hd[u]=cnt;
	return ;	
}
void topsort(int op){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(!deg[i])q.push(i);
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=hd[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			vis[v]|=vis[u];
			if(!(--deg[v])){
				q.push(v);
			}
		}
	}
	return ;
}
int main(){
	n=read();
	m=read();
	for(int i=1;i<=m;i++){
		x=read();
		y=read();
		deg[x]++;
		add(y,x);	
	}
	for(int i=1;i<=n;i++){
		ans-=deg[i]+1;
		out[i]=deg[i];	
	}
	limit=n/2;
	for(int i=1;i<=limit;i++){
		vis[i][i]=1;
	}
	topsort(0);
	for(int i=1;i<=n;i++){
		ans+=vis[i].count();
//		cout<<vis[i].count()<<endl;	
		vis[i].reset();
		deg[i]=out[i];
	}
//	vis.reset();
	for(int i=limit+1;i<=n;i++){
		vis[i][i-limit]=1;
	}
	topsort(1);
	for(int i=1;i<=n;i++){
//		cout<<vis[i].count()<<" ";
		ans+=vis[i].count();	
	}
	cout<<ans;
	return 0;
}

/*
5 5
1 2
1 3
2 3
3 4
4 5
*/

B. 时间机器

说实话考场上没有认真看懂题,光用网络流水了一下居然拿到了70分

这道题正解贪心
考虑所有元件和节点按左端点第一关键字右端点第二关键字排序后,那么右端点靠左的元件越容易用不上,所以尽可能优先选取右端点靠左的元件
那么对于一个节点拥有一个左右端点满足条件的候选集合,考虑左端点对于后面的节点都是可用的,但是右端点可能会被淘汰掉,那么就选择右端点最靠左的一个元件进行匹配
这个可以用 \(set\) 实现

注意这道题一开始一直被卡常,后来发现 \(s.lower\_bound(xxx)\)\(lower\_bound(s.begin(),s.end(),xxx)\) 快好多

代码实现
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int maxn=1e6+5;
int t,n,m,tp;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();	
	}
	return x*f;
}
struct Th{
	int l,r,num;	
}a[maxn],b[maxn];
struct Node{
	int fi;
	mutable int se;
	Node(int x=0,int y=0):fi(x),se(y){}
};
bool operator < (const Node &x,const Node &y){
	return x.fi!=y.fi?x.fi<y.fi:x.se<y.se;
}
multiset<Node>s;
bool operator < (const Th &x,const Th &y){
	return x.l==y.l?x.r<y.r:x.l<y.l;	
}
void solve(){
	tp=1;
	for(int i=1;i<=n;i++){
		while(tp<=m&&b[tp].l<=a[i].l){
			s.insert(Node(b[tp].r,b[tp].num));
			tp++;
		}
		while(a[i].num){
			multiset<Node>::iterator it=s.lower_bound(Node(a[i].r,0));
			if(it==s.end()){
				puts("No");
				return ;
			}
			if(a[i].num < it->se){
				it->se -= a[i].num;
				break;
			}
			a[i].num-= it->se;	
			s.erase(it);
		}
	}
	puts("Yes");
}
signed main(){
	t=read();
	while(t--){
		n=read();
		m=read();
		for(int i=1;i<=n;i++){
			a[i].l=read();
			a[i].r=read();
			a[i].num=read();	
		}
		for(int i=1;i<=m;i++){
			b[i].l=read();
			b[i].r=read();
			b[i].num=read();
		}
		sort(a+1,a+n+1);
		sort(b+1,b+m+1);
		s.clear();
		solve();
	}
	return 0;
}

/*
3
2 2
1 2 2
1 2 1
1 2 1
1 2 2
2 2
1 4 2
3 5 1
1 4 2
2 5 1

3 2
1 3 1
2 4 1
3 5 1
1 3 2
2 5 1

*/

C. weight

考完试 cyh 说和题单上的 Mst 这道题基本上是一样的,真后悔当时没看这道题

首先把最小生成树建出来,然后树边和非树边分别讨论

如果是非树边,那么答案为生成树上两端点路径上边权最大值 -1
如果是树边,则是所有和这条边有关的非树边的最小值 -1

可以采用树剖,每条非树边查询一次修改一次即可

cyh 的LCT理论复杂度少一个 \(log\),也非常好维护

注意判不在连通块里的边(可以用并查集判断),树剖需要边化点,更新时不能选上面的那个点
于是一直调一直改写了将近300行……

代码实现

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int maxm=4e5+5;
//const int inf=
int n,m,ans[maxn],x,y,w,op;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();	
	}
	return x*f;
}
namespace p2{
	int hd[maxn],cnt,cnt1,re[maxn],fa[maxn],tot,id[maxn],pa[maxn],siz[maxn],son[maxn],dep[maxn],tp[maxn],val[maxn];
	struct Edge{
		int nxt,to,val,id;	
	}edge[maxm];
	void add(int u,int v,int w,int num){
		edge[++cnt].nxt=hd[u];
		edge[cnt].to=v;
		edge[cnt].val=w;
		edge[cnt].id=num;
		hd[u]=cnt;
		return ;
	}
	struct Ch{
		int from,to,id,val;	
	}link[maxm];
	void make(int u,int v,int w,int num){
		link[++cnt1].to=v;
		link[cnt1].from=u;
		link[cnt1].val=w;
		link[cnt1].id=num;
		return ;
	}
	void dfs(int u){
//		cout<<u<<" "<<endl;
		siz[u]=1;
		for(int i=hd[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(v==fa[u])continue;
			dep[v]=dep[u]+1;
			fa[v]=u;
			val[v]=edge[i].val;
			pa[v]=edge[i].id;
//			cout<<"hhh "<<v<<" "<<pa[v]<<endl;
			dfs(v);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;	
		}
		return ;
	}	
	void dfs1(int u,int top){
		tp[u]=top;
		id[u]=++tot;
		re[tot]=u;
//		cout<<u<<" ";
		if(son[u])dfs1(son[u],top);
		for(int i=hd[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(v==fa[u]||v==son[u])continue;
			dfs1(v,v);
		}
		return ;
	}
	struct Seg{
		int l,r,mx,lazy;	
	}t[maxn*4];
	void update(int p){
		t[p].mx=max(t[p<<1].mx,t[p<<1|1].mx);
		return ;	
	}
	void build(int p,int l,int r){
		t[p].l=l;
		t[p].r=r;
		t[p].lazy=0x3f3f3f3f;
		if(l==r){
			t[p].mx=val[re[l]];
//			cout<<"kkk "<<l<<" "<<val[id[l]]<<endl;
			return ;
		}
		int mid=l+r>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		update(p);
		return ;
	}
	int ask(int p,int l,int r){
		if(l>r)return 0;
		if(t[p].l>=l&&t[p].r<=r)return t[p].mx;
		int mx=0;
		int mid=t[p].l+t[p].r>>1;
		if(l<=mid)mx=ask(p<<1,l,r);
		if(r>mid)mx=max(mx,ask(p<<1|1,l,r));
		return mx;
	}
	int ques(int x,int y){
		int mx=0;
//		cout<<"hhh "<<endl;
		while(tp[x]!=tp[y]){
			if(dep[tp[x]]<dep[tp[y]])swap(x,y);
//			cout<<"ppp "<<id[tp[x]]<<" "<<id[x]<<endl;
			mx=max(mx,ask(1,id[tp[x]],id[x]));
			x=fa[tp[x]];
		}
//		cout<<mx<<endl;
		if(dep[x]>dep[y])swap(x,y);
//		cout<<"ppp "<<id[x]+1<<" "<<id[y]<<endl;
		mx=max(mx,ask(1,id[x]+1,id[y]));
//		cout<<id[x]<<" "<<id[y]<<endl;
		return mx;
	}
	void ch(int p,int l,int r,int w){
		if(l>r)return ;
		if(t[p].l>=l&&t[p].r<=r){
			t[p].lazy=min(t[p].lazy,w);
			return ;	
		}
		int mid=t[p].l+t[p].r>>1;
		if(l<=mid)ch(p<<1,l,r,w);
		if(r>mid)ch(p<<1|1,l,r,w);
		return ;
	}
	void change(int x,int y,int w){
		
		while(tp[x]!=tp[y]){
			if(dep[tp[x]]<dep[tp[y]])swap(x,y);
			
			ch(1,id[tp[x]],id[x],w);
			x=fa[tp[x]];
		}
//		cout<<mx<<endl;
		if(dep[x]>dep[y])swap(x,y);
		
		ch(1,id[x]+1,id[y],w);
		return ;
	}
	void spread(int p){
		if(t[p].l==t[p].r){
//			cout<<t[p].l<<" "<<re[t[p].l]<<" "<<pa[re[t[p].l]]<<endl;
			ans[pa[re[t[p].l]]]=t[p].lazy;
			return ;	
		}
		t[p<<1].lazy=min(t[p<<1].lazy,t[p].lazy);
		t[p<<1|1].lazy=min(t[p<<1|1].lazy,t[p].lazy);
		spread(p<<1);
		spread(p<<1|1);
		return ;
	}
	void start(){
		dfs(1);
		pa[1]=m+1;
		dfs1(1,1);
		build(1,1,n);
//		for(int i=1;i<=n;i++){
//			cout<<pa[i]<<endl;	
//		}
//		cout<<endl;
//		cout<<"hhh ";
		for(int i=1;i<=cnt1;i++){
			int x=link[i].from;
			int y=link[i].to;
//			cout<<link[i].id<<" "<<x<<" "<<y<<" "<<link[i].val<<endl;
			ans[link[i].id]=ques(x,y);
//			flag[link[i].id];
//			cout<<ans[link[i].id]<<endl;
//			cout<<link[i].id<<" "<<ans[link[i].id]<<endl;
			change(x,y,link[i].val);	
		}
		spread(1);
		return ;
	}
}
namespace p1{
	int hd[maxn],cnt,fa[maxn];
	struct Edge{
		int nxt,to,from,val,id;	
	}edge[maxm],edge1[maxm];
	void add(int u,int v,int w){
		edge[++cnt].nxt=hd[u];
		edge[cnt].to=v;
		edge[cnt].from=u;
		edge[cnt].val=w;
		return ;
	}
	bool cmp(Edge a,Edge b){
		return a.val<b.val;
	}
	int find(int x){
		return fa[x]==x?x:fa[x]=find(fa[x]);	
	}
	void kruscal(){
		sort(edge+1,edge+m+1,cmp);
		for(int i=1;i<=n;i++){
			fa[i]=i;
		}
		for(int i=1;i<=m;i++){
			int u=edge[i].from;
			int v=edge[i].to;
			x=find(u);
			y=find(v);
			if(x!=y){
				fa[x]=y;
//				cout<<u<<" "<<v<<" "<<edge[i].val<<" "<<edge[i].id<<endl;
				p2::add(u,v,edge[i].val,edge[i].id);
				p2::add(v,u,edge[i].val,edge[i].id);
			}
			else{
//				cout<<u<<" "<<v<<" "<<edge[i].val<<" "<<edge[i].id<<endl;
				p2::make(u,v,edge[i].val,edge[i].id);
			}
		}
		return ;
	}
}
namespace p3{
	int fa[maxn],tot,tot1,idx[maxn];
	int find(int x){
		return fa[x]==x?x:fa[x]=find(fa[x]);	
	}
	void pd(){
		for(int i=1;i<=n;i++){
			fa[i]=i;
		}
		for(int i=1;i<=m;i++){
			int x=p1::edge[i].from;
			int y=p1::edge[i].to;
			x=find(x);
			y=find(y);
			if(x!=y)fa[x]=y;
		}
		for(int i=1;i<=m;i++){
			p1::edge1[i]=p1::edge[i];
//			cout<<find(p1::edge[i].from)<<" "<<find(1)<<endl;
			if(find(p1::edge[i].from)!=find(1)&&find(p1::edge[i].to)!=find(1)){
				ans[p1::edge[i].id]=1;
//				cout<<"ppp "<<p1::edge[i].id<<endl;
				tot++;
			}
			else idx[++tot1]=i;
		}
		p1::cnt=0;
		memset(p1::hd,0,sizeof p1::hd);
		for(int i=1;i<=tot1;i++){
			p1::add(p1::edge1[idx[i]].from,p1::edge1[idx[i]].to,p1::edge1[idx[i]].val);
			p1::edge[p1::cnt].id=p1::edge1[idx[i]].id;
		}
		m=tot1;
	}
}
int main(){
//	freopen("c.in","r",stdin);
//	freopen("c.out","w",stdout);
	n=read();
	m=read();
	op=read();
	for(int i=1;i<=m;i++){
		x=read();
		y=read();
		w=read();
		p1::add(x,y,w);
		p1::edge[p1::cnt].id=i;
	}
//	cout<<"hhh ";
//	p3::tot1=m;
	p3::pd();
//	cout<<"ppp "<<m<<endl;
//	cout<<"hhh ";
//	for(int i=1;i<=m;i++){
//		cout<<p1::edge[i].from<<" "<<p1::edge[i].to<<" "<<p1::edge[i].val<<" "<<p1::edge[i].id<<endl;	
//	}
	p1::kruscal();
//	cout<<"hhh ";
	p2::start();
	for(int i=1;i<=p3::tot+p3::tot1;i++){
		if(ans[i]==0x3f3f3f3f)printf("-1 ");
		else printf("%d ",ans[i]-1);
	}
	return 0;
}

/*
5 4 0
4 5 2
1 2 1
2 3 2
3 1 3

*/

\(\color{white}{\mathbb{困于心,衡于虑,而后作}}\)

posted @ 2021-07-18 11:51  y_cx  阅读(46)  评论(0编辑  收藏  举报