7.16考试总结(NOIP模拟17)[世界线·时间机器·weight]

车如流水马如龙,花月正春风

前言

其实,一开始 T1 是看错了题的,我以为是无向图来着,就想直接搞到每一个联通块的完全图,然后减去总边数就好了。

发现错误之后,码了个暴力,想得 40pts 来着,没想到 C++11O2 编辑器搞不了没有返回值的 int 函数,挂了40分。

对于 T2 第一眼就是一个贪心就可以搞定的,花的时间也比较少,但没想到是得分最多的(70pts)。

T3 的话,剩下时间也不多了,就看准了那个特殊性质,但是割边板子忘了,然后就又想到了下午做的有机化学之神,就码了一个边双,判断边的两个端点是不是在同一个边双里,然后就又挂了20分,只搞到了 10pts 。

T1 世界线

解题思路

正解是搞的拓扑排序(当然如果你精通各种 XIN_Team 算法,也不是卡不过)

主要思路是将每一个点的贡献下放到有连边的子节点上。

然后就是裸的 DAG 了,因为需要将两个集合合并起来,用 bitset 显然是最好的选择。

因为空间卡的比较紧,我们直接开一半的 bitset 用完之后回收就好了。

code

40pts 暴力

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int K=1e4+10,N=6e4+10,M=1e5+10;
int n,m,ans;
int tot,head[N],ver[M<<1],nxt[M<<1];
bitset<N/2> vis[N],b[N];
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int x,int top)
{
	if(!vis[top][x]&&top!=x)	ans++;
	if(b[top][x])	return ;
	vis[top][x]=1;
	b[top][x]=1;
	for(int i=head[x];i;i=nxt[i])
		dfs(ver[i],top);
}
signed main()
{
	n=read();
	m=read();
	for(int i=1,x,y;i<=m;i++)
	{
		x=read();
		y=read();
		add_edge(x,y);
		vis[x][y]=1;
	}
	for(int i=1;i<=n;i++)
		dfs(i,i);
	printf("%d\n",ans);
	return 0;
}

正解

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=6e4+10,M=1e5+10;
int n,m,ans,tot,du[N],id[N];
int cnt,bac[N];
vector<int> v[N];
bitset<N> s[N/2];
queue<int> q;
int New()
{
	int temp;
	if(cnt)
	{
		temp=bac[cnt--];
		s[temp].reset();
	}
	else	temp=++tot;
	return temp;
}
signed main()
{
	n=read();
	m=read();
	for(int i=1,x,y;i<=m;i++)
	{
		x=read();
		y=read();
		v[x].push_back(y);
		du[y]++;
	}
	for(int i=1;i<=n;i++)
		if(!du[i])
		{
			id[i]=New();
			q.push(i);
			s[id[i]][i]=1;
		}
	while(!q.empty())
	{
		int y=q.front();
		q.pop();
		ans+=s[id[y]].count()-1;
		for(int i=0;i<v[y].size();i++)
		{
			int to=v[y][i];
			if(!id[to])	id[to]=New();
			s[id[to]][to]=1;
			s[id[to]]|=s[id[y]];
			du[to]--;
			if(!du[to])	q.push(to);
		}
		bac[++cnt]=id[y];
	}
	printf("%d\n",ans-m);
	return 0;
}

T2 时间机器

解题思路

比较简单的一个题了,先对于电阻和电压都按照 low 排个序。

枚举电压,然后对于每一个 low 符合条件的电阻将 high 排一个序,贪心选择 high 较小的就行。

如果电压有剩余就证明不可行,反之就是对的。

正解就是在此基础上用 set 进行优化,有一种莫队的感觉。

只可惜我一时糊涂,在我这垃圾暴力上拼命优化(优化了个寂寞)。

后来发现这么优化不和暴力排序一样吗???????(我裂开,亏我还因为重载运算符的原因手写了一个二分)

还有就是如果你的 set 删除元素的时候传入的普通变量会删除掉所有的同样的变量,因此这样过不了样例(但是可以 A 题)

所以传的时候注意传地址,迭代器。

code

裸的70pts暴力

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=4e5+10;
int T,n,m,sta[N],top;
vector<int> v;
struct Node
{
	int hig,low,sum;
	void insert()
	{
		low=read();
		hig=read();
		sum=read();
	}
}u[N],r[N];
void solve1()
{
	int tot=0,sum=u[1].sum,hig=u[1].hig,low=u[1].low;
	for(int i=1;i<=m;i++)
		if(hig<=r[i].hig&&low>=r[i].low)
			tot+=r[i].sum;
	if(tot>=sum)	printf("Yes\n");
	else	printf("No\n");
}
bool comp(Node x,Node y)
{
	if(x.low!=y.low)	return x.low<y.low;
	if(x.hig!=y.hig)	return x.hig<y.hig;
	return x.sum>y.sum;
}
bool cmp(int x,int y)
{
	return r[x].hig>r[y].hig;
}
void solve2()
{
	sort(u+1,u+n+1,comp);
	sort(r+1,r+m+1,comp);
	for(int i=1;i<=n;i++)
	{
		top=0;
		for(int j=m;j>=1;j--)
		{
			if(!r[j].sum)	continue;
			if(r[j].low<=u[i].low)
				sta[++top]=j;
		}
		if(!top)
		{
			printf("No\n");
			return ;
		}
		sort(sta+1,sta+top+1,cmp);
		for(int j=1;j<=top;j++)
			if(r[sta[j]].hig<u[i].hig)
			{
				top=j-1;
				break;
			}
		if(!top)
		{
			printf("No\n");
			return ;
		}
		int tot=u[i].sum;
		for(int j=top;j>=1;j--)
		{
			if(!tot)	break;
			if(r[sta[j]].sum<=tot)
			{
				tot-=r[sta[j]].sum;
				r[sta[j]].sum=0;
				continue;
			}
			r[sta[j]].sum-=tot;
			tot=0;
			break;
		}
		if(tot)
		{
			printf("No\n");
			return ;
		}
	}
	printf("Yes\n");
}
signed main()
{
	T=read();
	while(T--)
	{
		n=read();
		m=read();
		for(int i=1;i<=n;i++)
			u[i].insert();
		for(int i=1;i<=m;i++)
			r[i].insert();
		if(n==1)	solve1();
		else	solve2();

	}
	return 0;
}

set 以及 二分优化的暴力

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=4e5+10,INF=1e18;
int T,n,m,sta[N],top;
vector<int> v;
struct Node
{
	int hig,low,sum,id;
	void insert()
	{
		low=read();
		hig=read();
		sum=read();
	}
	friend bool operator <(Node x,Node y)
	{
		return x.hig<y.hig;
	}
	friend bool operator ==(Node x,Node y)
	{
		return x.hig==y.hig;
	}
	friend bool operator >(Node x,Node y)
	{
		return x.hig>y.hig;
	}
}u[N],r[N];
multiset<Node> s;
bool comp(Node x,Node y)
{
	if(x.low!=y.low)	return x.low<y.low;
	if(x.hig!=y.hig)	return x.hig<y.hig;
	return x.sum>y.sum;
}
bool cmp(int x,int y)
{
	return r[x].hig>r[y].hig;
}
inline void solve2()
{
	sort(u+1,u+n+1,comp);
	sort(r+1,r+m+1,comp);
	for(int i=1;i<=n;i++)
		u[i].id=i;
	for(int i=1;i<=m;i++)
		r[i].id=i;
	for(int i=1;i<=n;i++)
	{
		top=m;
		int l=1,rr=m;
		while(l<=rr)
		{
			int mid=(l+rr)>>1;
			if(r[mid].low>u[i].low)	rr=mid-1;
			else	l=mid+1,top=mid;
		}
		for(int j=1;j<=m;j++)
			if(r[j].low>u[i].low)
			{
				top=j-1;
				break;
			}
		if(!top)
		{
			printf("No\n");
			return ;
		}
		s.clear();
		for(int j=1;j<=top;j++)
			if(r[j].sum)
				s.insert(r[j]);
		s.insert((Node){INF,0,0,0});
		multiset<Node>::iterator it=s.lower_bound(u[i]);
		Node temp=*it;
		if(temp.hig==INF)
		{
			printf("No\n");
			return ;
		}
		if(temp.hig<u[i].hig)
			it++;
		int tot=u[i].sum;
		for(multiset<Node>::iterator j=it;j!=s.end();j++)
		{
			temp=*j;
			if(temp.hig==INF)	break;
			if(!tot)	break;
			if(!temp.sum)	continue;
			if(temp.sum<=tot)
			{
				tot-=temp.sum;
				r[temp.id].sum=0;
				continue;
			}
			r[temp.id].sum-=tot;
			tot=0;
			break;
		}
		if(tot)
		{
			printf("No\n");
			return ;
		}
	}
	printf("Yes\n");
}
signed main()
{
	T=read();
	while(T--)
	{
		n=read();
		m=read();
		for(int i=1;i<=n;i++)
			u[i].insert();
		for(int i=1;i<=m;i++)
			r[i].insert();
		solve2();
	}
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=4e5+10,INF=1e18;
int T,n,m,sta[N],top;
vector<int> v;
struct Node
{
	int hig,low,sum,id;
	void insert()
	{
		low=read();
		hig=read();
		sum=read();
	}
	friend bool operator <(Node x,Node y)
	{
		return x.hig<y.hig;
	}
	friend bool operator ==(Node x,Node y)
	{
		return x.hig==y.hig;
	}
	friend bool operator >(Node x,Node y)
	{
		return x.hig>y.hig;
	}
}u[N],r[N];
multiset<Node> s;
bool comp(Node x,Node y)
{
	if(x.low!=y.low)	return x.low<y.low;
	if(x.hig!=y.hig)	return x.hig<y.hig;
	return x.sum>y.sum;
}
bool cmp(int x,int y)
{
	return r[x].hig>r[y].hig;
}
inline void solve2()
{
	s.clear();
	s.insert((Node){INF,0,0,0});
	sort(u+1,u+n+1,comp);
	sort(r+1,r+m+1,comp);
	for(int i=1;i<=n;i++)
		u[i].id=i;
	for(int i=1;i<=m;i++)
		r[i].id=i;
	bool judge=false;
	for(int i=1,j=1;i<=n;i++)
	{
		while(j<=m&&r[j].low<=u[i].low)
		{
			s.insert(r[j]);
			j++;
		}
		while(u[i].sum&&!s.empty())
		{
			multiset<Node>::iterator it=s.lower_bound(u[i]);
			Node temp=*it;
			if(temp.hig==INF)
			{
				judge=true;
				break;
			}
			s.erase(it);
			if(temp.sum<=u[i].sum)
				u[i].sum-=temp.sum;
			else
			{
				temp.sum-=u[i].sum;
				u[i].sum=0;
				s.insert(temp);
			}
		}
		if(u[i].sum)	judge=true;
		if(judge)	break;
	}
	if(judge)	printf("No\n");
	else	printf("Yes\n");
}
signed main()
{
	T=read();
	while(T--)
	{
		n=read();
		m=read();
		for(int i=1;i<=n;i++)
			u[i].insert();
		for(int i=1;i<=m;i++)
			r[i].insert();
		solve2();
	}
	return 0;
}

T3 weight

解题思路

这个题好像和前两道并不是一套的,而且非常的恶心人。

特殊性质就不多说了,下面会给出代码。

正解是先搞出一棵最小生成树,对于树边和非树边进行分类讨论。

  • 非树边

    至少要将它的权证调整到树上两个端点对应路径边权最大值-1才可以,否则这条边就一定不会被选上。

  • 树边

    对于两个端点的简单路径经过树的哪些边,与非树边相反,我们应该选择那些边中最小权值-1

    只有这样才可以保证它在最小生成树上。

对于上面的操作可以进行树链剖分,然后用线段树维护,进行区间修改,单点查询等一系列操作。

code

特殊性质

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=7e4+10,M=1e5+10;
int n,m,cnt,total,task,ans[M],fa[N];
int tot=1,head[N],ver[M<<1],nxt[M<<1],edge[M<<1];
int tim,dfn[N],low[N];
bool vis[M<<1];
struct Node
{
	int id,l,r;
}pat[M],rod[M];
void add_edge(int x,int y,int v)
{
	ver[++tot]=y;
	edge[tot]=v;
	nxt[tot]=head[x];
	head[x]=tot;
}
int find(int x)
{
	if(fa[x]==x)	return x;
	return fa[x]=find(fa[x]);
}
inline void tarjan(int x,int fro)
{
	dfn[x]=low[x]=++tim;
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i];
		if(!dfn[to])
		{
			tarjan(to,i);
			low[x]=min(low[x],low[to]);
			if(low[to]>dfn[x])
			{
				vis[i]=vis[i^1]=true;
			}
		}
		else	if(i!=(fro^1))
			low[x]=min(low[x],dfn[to]);
	}
}
void solve1()
{
	for(int i=1,val;i<=m;i++)
	{
		pat[i].l=read();
		pat[i].r=read();
		val=read();
		pat[i].id=i;
		add_edge(pat[i].l,pat[i].r,val);
		add_edge(pat[i].r,pat[i].l,val);
	}
	tarjan(1,0);
	for(int i=2;i<=tot;i+=2)
		if(vis[i])
			ans[i/2]=-1;
	for(int i=1;i<=m;i++)
		printf("%lld ",ans[i]);
}
signed main()
{
	n=read();
	m=read();
	task=read();
	if(task)	solve1();
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10,M=1e5+10,INF=1e9;
int n,m,task;
int tim,dfn[N],topp[N],siz[N],son[N],dep[N],fat[N],id[N];
int fa[N],s[N],ans[N];
bool vis[N];
struct Edge
{
	int tot,id[M<<1],head[N],ver[M<<1],nxt[M<<1],edge[M<<1];
	void add(int x,int y,int val,int pos)
	{
		ver[++tot]=y;
		edge[tot]=val;
		id[tot]=pos;
		nxt[tot]=head[x];
		head[x]=tot;
	}
}e1,e2;
struct Road
{
	int l,r,id,val;
	void insert(int pos)
	{
		l=read();
		r=read();
		val=read();
		id=pos;
	}
}pat[M],rod[M];
struct Segment_Tree
{
	int mn,mx,laz;
}tre[N<<2];
int nod[M],sid[N],root;
void dfs1(int x)
{
	siz[x]=1;
	for(int i=e1.head[x];i;i=e1.nxt[i])
	{
		int to=e1.ver[i];
		if(siz[to])	continue;
		dep[to]=dep[x]+1;
		fat[to]=x;
		dfs1(to);
		siz[x]+=siz[to];
		if(siz[to]>siz[son[x]])
		{
			sid[x]=i;
			son[x]=to;
		}
	}
}
void dfs2(int x,int tp)
{
	topp[x]=tp;
	dfn[x]=++tim;
	id[tim]=x;
	if(son[x])
	{
		s[son[x]]=e1.edge[sid[x]];
		nod[e1.id[sid[x]]]=son[x];
		dfs2(son[x],tp);
	}
	for(int i=e1.head[x];i;i=e1.nxt[i])
		if(!dfn[e1.ver[i]])
		{
			s[e1.ver[i]]=e1.edge[i];
			nod[e1.id[i]]=e1.ver[i];
			dfs2(e1.ver[i],e1.ver[i]);
		}
}
bool comp(Road x,Road y)
{
	return x.val<y.val;
}
int find(int x)
{
	if(fa[x]==x)	return x;
	return fa[x]=find(fa[x]);
}
void Kruskal()
{
	for(int i=1;i<=m;i++)
		rod[i]=pat[i];
	for(int i=1;i<=n;i++)
		fa[i]=i;
	sort(rod+1,rod+m+1,comp);
	for(int i=1;i<=m;i++)
	{
		int x=find(rod[i].l);
		int y=find(rod[i].r);
		if(x==y)	continue;
		fa[x]=y;
		vis[rod[i].id]=true;
		e1.add(rod[i].l,rod[i].r,rod[i].val,rod[i].id);
		e1.add(rod[i].r,rod[i].l,rod[i].val,rod[i].id);
	}
	root=fa[find(1)];
}
void push_down(int x)
{
	if(tre[x].laz==INF)	return ;
	tre[ls].laz=min(tre[ls].laz,tre[x].laz);
	tre[ls].mn=min(tre[ls].mn,tre[x].laz);
	tre[rs].laz=min(tre[rs].laz,tre[x].laz);
	tre[rs].mn=min(tre[rs].mn,tre[x].laz);
	tre[x].laz=INF;
}
void build(int x,int l,int r)
{
	tre[x].mn=tre[x].laz=INF;
	if(l==r)
	{
		tre[x].mx=s[id[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	tre[x].mx=max(tre[ls].mx,tre[rs].mx);
}
void update(int x,int l,int r,int L,int R,int num)
{
	if(L>R)	return ;
	if(L<=l&&r<=R)
	{
		tre[x].mn=min(tre[x].mn,num);
		tre[x].laz=min(tre[x].laz,num);
		return ;
	}
	push_down(x);
	int mid=(l+r)>>1;
	if(L<=mid)	update(ls,l,mid,L,R,num);
	if(R>mid)	update(rs,mid+1,r,L,R,num);
	return ;
}
int query_max(int x,int l,int r,int L,int R)
{
	if(L>R)	return 0;
	if(L<=l&&r<=R)
		return tre[x].mx;
	int mid=(l+r)>>1,maxn=0;
	if(L<=mid)	maxn=query_max(ls,l,mid,L,R);
	if(R>mid)	maxn=max(maxn,query_max(rs,mid+1,r,L,R));
	return maxn;
}
int query(int x,int l,int r,int pos)
{
	if(l==r)	return tre[x].mn==INF?-1:tre[x].mn;
	int mid=(l+r)>>1;
	push_down(x);
	if(pos<=mid)	return query(ls,l,mid,pos);
	return query(rs,mid+1,r,pos);
}
void solve(int x,int y,int val,int pos)
{
	if(!topp[x]||!topp[y])	return ;
	while(topp[x]^topp[y])
	{
		if(dep[topp[x]]<dep[topp[y]])
			swap(x,y);
		update(1,1,tim,dfn[topp[x]],dfn[x],val);
		ans[pos]=max(ans[pos],query_max(1,1,tim,dfn[topp[x]],dfn[x])-1);
		x=fat[topp[x]];
	}
	if(dep[x]<dep[y])	swap(x,y);
	update(1,1,tim,dfn[y]+1,dfn[x],val);
	ans[pos]=max(ans[pos],query_max(1,1,tim,dfn[y]+1,dfn[x])-1);
}
signed main()
{
//	freopen("date.in","r",stdin);
	n=read();
	m=read();
	task=read();
	for(int i=1;i<=m;i++)
		pat[i].insert(i);
	Kruskal();
	dfs1(1);
	dfs2(1,1);
	build(1,1,tim);
	for(int i=1;i<=m;i++)
		if(vis[i]==0)
			solve(pat[i].l,pat[i].r,pat[i].val-1,i);
	for(int i=1;i<=m;i++)
	{
		if(find(pat[i].l)!=root||find(pat[i].r)!=root)
		{
			ans[i]=0;;
			vis[i]=false;
		}
		if(vis[i])
			ans[i]=query(1,1,tim,dfn[nod[i]]);
	}
	for(int i=1;i<=m;i++)
		printf("%lld ",ans[i]);
	return 0;
}
posted @ 2021-07-18 12:08  Varuxn  阅读(64)  评论(0编辑  收藏  举报