noip模拟测试83

Posted WindZR

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了noip模拟测试83相关的知识,希望对你有一定的参考价值。

考试过程:这次考试,总体来说难度是不大的,我觉得前两题是可做的,首先是第一题,我觉得是个线段树板子题,也没多想,就是数据范围有点大,但是我没什么好方法优化,就打了个线段树走了。
然后是T2,首先想出了\\(o(n\\times log(n))\\)求出以1为根的答案,然后考虑移动。不难发现,当根节点移动一次时,答案相对于上一次的变化量由两部分组成:
1.当前点为根的子树内的点的\\(w\\)小于上一次的根节点的\\(w\\)的数量。
2.除去当前点为根的子树,剩下的点中\\(w\\)小于当前根节点的数量。
但是我在考场上想复杂了,这是我经过优化之后的想法,当时的做法还要考虑每个子树对当前点的贡献,比较复杂,打了个树套树,但是卡在最后一个问题没有解决。就是如何在总复杂度在\\(O(n\\times log(n))\\)左右求出以每个点为根的子树内\\(w\\)值小于当前根节点的点的个数。
考虑解决这个问题:因为主席树可以支持查询区间第\\(k\\)大,那么我们可以在\\(dfs\\)序上建一颗主席树,那么显然我们把\\(w\\)数组经过离散化之后就知道了\\(w\\)的排名,那么我们直接在主席树上查询即可。
剩下的T3,T4没什么时间做了,就打了个暴力。

T1 树上的数

思路:因为5e6的数据带个\\(log\\)达到了\\(1e8\\)级别,在那个超快的评测机上根本过不去,但是线段树无法进行优化,考虑另外的思路。
思考如果我们要打暴力,那么应该是从当前根节点往下递归,统计出当前子树的大小。
考虑优化这个过程,我们可以对经过的节点打上标记,这样我们就不会经过重复的节点,这样的复杂度是\\(o(n+m)\\)的。
代码如下:

AC_code

#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
#define next netetet
#define head heaeaea
using namespace std;
const int N=5e6+10;
int n,m,tot,ans,sum;
int to[N],next[N],head[N],Q[N];
int fa,q,l,r;
bool vis[N];
ii read()
{
	int x=0;char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'-\') f=0;
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?(x):(-x);
}
iv check(int st)
{
	vis[st]=1;--sum;
	for(re i=head[st];i;i=next[i]) if(!vis[to[i]]) check(to[i]); 
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	int a,b,x,y;
	n=read(),m=read();
	a=read(),b=read();
	fa=1;
	to[++tot]=2;
	next[tot]=head[1];
	head[1]=tot;
	for(re i=3;i<=n;++i)
	{
		fa=((1ll*fa*a+b)^19760817)%(i-1)+1;
		to[++tot]=i;
		next[tot]=head[fa];
		head[fa]=tot;
	}
	q=read(),x=read(),y=read();
	sum=n;
	for(re i=1;i<=m;i++)
	{
		if(!sum) break;
		if(i!=1) q=(((1ll*q*x+y)^19760817)^(i<<1))%(n-1)+2;
		if(!vis[q])
		{
			l=1,r=0;
			Q[++r]=q;
			while(l<=r)
			{
				int now=Q[l++];
				vis[now]=1;
				--sum;
				for(re j=head[now];j;j=next[j])
				{
					if(!vis[to[j]]) Q[++r]=to[j];
				}	
			}
		}
		ans^=sum;
	}
	printf("%d\\n",ans);
	return 0;
}


T2 时代的眼泪

思路:首先想出了\\(o(n\\times log(n))\\)求出以1为根的答案,然后考虑移动。不难发现,当根节点移动一次时,答案相对于上一次的变化量由两部分组成:
1.当前点为根的子树内的点的\\(w\\)小于上一次的根节点的\\(w\\)的数量。
2.除去当前点为根的子树,剩下的点中\\(w\\)小于当前根节点的数量。
但是我在考场上想复杂了,这是我经过优化之后的想法,当时的做法还要考虑每个子树对当前点的贡献,比较复杂,打了个树套树,但是卡在最后一个问题没有解决。就是如何在总复杂度在\\(O(n\\times log(n))\\)左右求出以每个点为根的子树内\\(w\\)值小于当前根节点的点的个数。
考虑解决这个问题:因为主席树可以支持查询区间第\\(k\\)大,那么我们可以在\\(dfs\\)序上建一颗主席树,那么显然我们把\\(w\\)数组经过离散化之后就知道了\\(w\\)的排名,那么我们直接在主席树上查询即可。
代码如下:

AC_code

#include<bits/stdc++.h>
#define ll long long
#define re int
#define ii inline int
#define iv inline void
#define f() cout<<"fuck"<<endl
#define next netetetetet
using namespace std;
const int N=1e6+10;
int n,q,tot,cnt,timi,zx;
int w[N],fa[N],lsh[N],dfn[N],size[N],root[N];
ll ans[N],he[N];
int to[N<<1],next[N<<1],head[N];
ii read()
{
	int x=0;char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'-\') f=0;
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?(x):(-x);
}
iv add(int x,int y)
{
	to[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
}
struct Segment_Tree
{
	#define mid ((l+r)>>1)
	int sum[N<<2],lc[N<<2],rc[N<<2];
	ii insert(int las,int l,int r,int p)
	{
		int now=++zx;
		lc[now]=lc[las];
		rc[now]=rc[las];
		if(l==r)
		{
			sum[now]=sum[las]+1;
			return now;
		}
		if(mid>=p) lc[now]=insert(lc[las],l,mid,p);
		else rc[now]=insert(rc[las],mid+1,r,p);
		sum[now]=sum[lc[now]]+sum[rc[now]];
		return now;
	}
	ll query(int las,int now,int l,int r,int z)
	{
		if(l==r) return (l==z)?0:sum[now]-sum[las];
		int out=0;
		if(mid<z)
			return sum[lc[now]]-sum[lc[las]]+query(rc[las],rc[now],mid+1,r,z);
		return query(lc[las],lc[now],l,mid,z);
	}
	#undef mid
}T;
struct Seg
{
	int sum[N<<1];
	iv add(int x) {for(;x<=cnt;x+=(x&(-x))) ++sum[x];}
	iv del(int x) {for(;x<=cnt;x+=(x&(-x))) --sum[x];}
	ll query(int x)
	{
		int out=0;
		for(;x;x-=(x&(-x))) out+=sum[x];
		return out;
	}		
}S;
iv dfs(int st,int f)
{
	dfn[st]=++timi;
	size[st]=1;
	root[timi]=T.insert(root[timi-1],1,cnt,w[st]);
	S.add(w[st]);
	ans[1]+=S.query(cnt)-S.query(w[st]);
	for(re i=head[st];i;i=next[i])
	{
		int p=to[i];
		if(p==f) continue;
		dfs(p,st);
		size[st]+=size[p];
		S.del(w[p]);
	}
}
iv check(int st,int f)
{
	if(st!=1)
	{
		ll now=ans[f];
		now-=T.query(root[dfn[st]-1],root[dfn[st]+size[st]-1],1,cnt,w[f]);
		now+=he[w[st]-1]-T.query(root[dfn[st]-1],root[dfn[st]+size[st]-1],1,cnt,w[st]);
		ans[st]=now;
	}
	for(re i=head[st];i;i=next[i])
	{
		if(to[i]==f) continue;
		check(to[i],st);
	}
}
signed main()
{
	freopen("tears.in","r",stdin);
	freopen("tears.out","w",stdout);
	n=read(),q=read();
	for(re i=1;i<=n;i++) w[i]=read(),lsh[i]=w[i];
	sort(lsh+1,lsh+n+1);
	cnt=unique(lsh+1,lsh+n+1)-lsh-1;
	for(re i=1;i<=n;i++)
	{
		w[i]=lower_bound(lsh+1,lsh+cnt+1,w[i])-lsh;
		++he[w[i]];	
	}
	for(re i=1;i<=cnt;i++) he[i]+=he[i-1];
	int x,y;
	for(re i=1;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
	dfs(1,0);
	check(1,0);
	while(q--)
	{
		x=read();
		printf("%lld\\n",ans[x]);
	}
	return 0;
}

T3 传统艺能

思路,先考虑没有修改的情况,设\\(f_{i,a}\\)表示考虑前\\(i\\)个位置,以\\(a\\)为结尾的方案数,那么转移是这样的\\(f_{i,a}=f_{i-1,a}+f_{i-1,b}+f_{i-1,c}+1,f_{i,b}=f_{i-1,b},f_{i,c}=f_{i-1,c}\\),其余情况同理。
那么这样的转移是\\(o(n)\\)的,考虑优化。不难发现我们的转移可以写成矩阵乘的形式,
\\(A\\)结尾为例:
1 0 0 0
1 1 0 0
1 0 1 0
1 0 0 1
一到三行分别表示\\(A,B,C\\),最后一行表示\\(1\\).
因为有修改和区间查询,那么我们可以考虑用线段树维护这个东西。
线段树的每个叶子几点都是一个矩阵,上面的节点维护矩阵相乘的结果。那么修改操作就是单点修改。
对于询问操作,我们可以定义一个初始矩阵\\(0 0 0 1\\)表示\\(A,B,C,1\\),将初始矩阵与线段树进行区间查询的矩阵相乘即可。但是我们发现答案就是线段树上矩阵的第四行的前三列的加和,那么我们直接输出即可。
代码如下:

AC_code

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define re int
#define ii inline int
#define iv inline void
#define f() cout<<"fuck"<<endl
using namespace std;
const int N=1e5+10;
const int mo=998244353;
int n,m;
long long ans;
char s[N],c[N];
ii read()
{
	int x=0;char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'-\') f=0;
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?(x):(-x);
}
struct mat
{
	int a[5][5];
};
mat calc(mat x,mat y)
{
	mat z;
	memset(z.a,0,sizeof(z.a));
	for(re i=1;i<=4;i++)
	{
		for(re j=1;j<=4;j++)
		{
			for(re k=1;k<=4;k++)
			{
				z.a[i][j]=(z.a[i][j]+1ll*x.a[i][k]*y.a[k][j])%mo;
			}
		}
	}
	return z;
}
struct Segment_Tree
{
	#define mid ((l+r)>>1)
	#define lc (rt<<1)
	#define rc (rt<<1|1)
	mat sum[N<<2];
	iv pp(int rt)
	{
		sum[rt]=calc(sum[lc],sum[rc]);
	}
	iv build(int rt,int l,int r)
	{
		if(l==r)
		{
			if(s[l]==\'A\')
			{
				for(re i=1;i<=4;i++)
				{
					for(re j=1;j<=4;j++)
					{
						if(i==1) sum[rt].a[j][i]=1;
						else
						{
							if(j==i) sum[rt].a[j][i]=1;
							else sum[rt].a[j][i]=0;
						}
					}
				}
			}
			else if(s[l]==\'B\')
			{
				for(re i=1;i<=4;i++)
				{
					for(re j=1;j<=4;j++)
					{
						if(i==2) sum[rt].a[j][i]=1;
						else
						{
							if(j==i) sum[rt].a[j][i]=1;
							else sum[rt].a[j][i]=0;
						}
					}
				}
			}
			else
			{
				for(re i=1;i<=4;i++)
				{
					for(re j=1;j<=4;j++)
					{
						if(i==3) sum[rt].a[j][i]=1;
						else
						{
							if(j==i) sum[rt].a[j][i]=1;
							else sum[rt].a[j][i]=0;
						}
					}
				}
			}
			return;	
		}
		build(lc,l,mid),build(rc,mid+1,r);
		pp(rt);
	}
	iv change(int rt,int l,int r,int p)
	{
		if(l==r)
		{
			if(s[l]==\'A\')
			{
				for(re i=1;i<=4;i++)
				{
					for(re j=1;j<=4;j++)
					{
						if(i==1) sum[rt].a[j][i]=1;
						else
						{
							if(j==i) sum[rt].a[j][i]=1;
							else sum[rt].a[j][i]=0;
						}
					}
				}
			}
			else if(s[l]==\'B\')
			{
				for(re i=1;i<=4;i++)
				{
					for(re j=1;j<=4;j++)
					{
						if(i==2) sum[rt].a[j][i]=1;
						else
						{
							if(j==i) sum[rt].a[j][i]=1;
							else sum[rt].a[j][i]=0;
						}
					}
				}
			}
			else
			{
				for(re i=1;i<=4;i++)
				{
					for(re j=1;j<=4;j++)
					{
						if(i==3) sum[rt].a[j][i]=1;
						else
						{
							if(j==i) sum[rt].a[j][i]=1;
							else sum[rt].a[j][i]=0;
						}
					}
				}
			}
			return;	
		}
		if(mid>=p) change(lc,l,mid,p);
		else change(rc,mid+1,r,p);
		pp(rt);	
	}
	mat query(int rt,int l,int r,int L,int R)
	{
		if(L<=l and r<=R) return sum[rt];
		if(mid>=R) return query(lc,l,mid,L,R);
		if(mid<L) return query(rc,mid+1,r,L,R);
		return calc(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R));
	}
	#undef mid
	#undef lc
	#undef rc
}T;
signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n=read(),m=read();
	scanf("%s",s+1);
	T.build(1,1,n);
	int opt,x,y;
	while(m--)
	{
		opt=read();
		if(opt==1)
		{
			x=read();
			scanf("%s",c+1);
			if(s[x]==c[1]) continue;
			s[x]=c[1];
			T.change(1,1,n,x);
		}
		else
		{
			x=read(),y=read();
			mat ans=T.query(1,1,n,x,y);
			printf("%lld\\n",(ans.a[4][1]+ans.a[4][2]+ans.a[4][3])%mo);
		}
	}
	return 0;
}

T4 铺设道路


注意是差分的过程,所以要算\\(n+1\\)项。
代码如下:

AC_code

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define re int
#define ii inline int
#define iv inline void
#define f() cout<<"fuck"<<endl
using namespace std;
const int N=3e5+10;
const int mo=1e9+7;
int n,ans,mx,mn;
int d[N],b[N],c[N];
queue<int> q;
stack<int> s;
ii read()
{
	int x=0;char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'-\') f=0;
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?(x):(-x);
}
signed main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	n=read();
	for(re i=1;i<=n;i++) d[i]=read();
	for(re i=1;i<=n+1;i++) b[i]=d[i]-d[i-1],c[i]=b[i];
	for(re i=1;i<=n+1;i++) ans=ans+max(0ll,b[i]);
	printf("%lld\\n",ans);
	for(re i=1;i<=n+1;i++)
	{
		if(b[i]>0) q.push(i),s.push(i);
		else
		{
			if(b[i]==0) continue;
			int tmp=-b[i];
			while(tmp>0 and q.size())
			{
				if(b[q.front()]<=tmp)
				{
					tmp-=b[q.front()];
					mx=(mx+(i-q.front())*(i-q.front())%mo*b[q.front()])%mo;
					q.pop();
				}
				else
				{
					mx=(mx+(i-q.front())*(i-q.front())%mo*(tmp))%mo;
					b[q.front()]-=tmp;
					break;
				}
			}
			tmp=-c[i];
			while(tmp>0 and s.size())
			{
				if(c[s.top()]<=tmp)
				{
					tmp-=c[s.top()];
					mn=(mn+(i-s.top())*(i-s.top())%mo*c[s.top()])%mo;
					s.pop();
				}
				else
				{
					mn=(mn+(i-s.top())*(i-s.top())%mo*(tmp))%mo;
					c[s.top()]-=tmp;
					break;
				}
			}
		}
	}
	printf("%lld\\n%lld\\n",mn,mx);
	return 0;
}

2021.11.18-NOIP模拟测试信心赛

2021.11.18-NOIP模拟信心赛

前言

太蒟蒻了,信心赛打的都快没信心了,giao

T1\\(\\color{green}100\\)题目

T1作为简单的签到题,直接先枚举所有的流量再用dijkstra跑这么多遍就可以了

#include<bits/stdc++.h>
#define M 2100
#define N 1100
#define inf 0x7f7f7f7f
using namespace std;
int n,m,maxx=-15;
int first[M],nex[M],to[M],w[M],f[M],tot;
int dis[N];
bool vis[N];
priority_queue<pair<int,int> >q;
//=======================================================
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
	return p*f;
}
//========================================================
inline void add(int x,int y,int z,int q){
	nex[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
	f[tot]=q;
}
//========================================================
inline void dijkstra(){
	for(int x=1;x<=1000;x++){
		for(int i=1;i<=n;i++){
			dis[i]=inf;
			vis[i]=0;
		}
		q.push(make_pair(0,1));
		dis[1]=0;	
		while(!q.empty()){
			int u=q.top().second;
			q.pop();
			if(vis[u])continue;
			vis[u]=1;
			for(int i=first[u];i;i=nex[i]){
				int v=to[i];
				if(f[i]<x)continue;
				if(dis[u]+w[i]<dis[v]){
					dis[v]=dis[u]+w[i];
					if(!vis[v]){
						q.push(make_pair(-dis[v],v));
					}
				}
			}
		}
		if(dis[n]!=inf)
			maxx=max(maxx,x*1000000/dis[n]);
	}
}
//========================================================
int main(){
	n=read(),m=read();
	int a,b,c,f;
	for(int i=1;i<=m;i++){
		a=read(),b=read(),c=read(),f=read();
		add(a,b,c,f);
		add(b,a,c,f);
	}
	dijkstra();
	cout<<maxx<<endl;
	return 0;
}

T2\\(\\color{red}0\\)题目

明明第二天比第一题还简单简单我却做不出来

一开始在哪里想建图然后\\(bfs\\)然后小样例过了,大样例却挂了

结果直接用一个并查集分成两个集合就好了(有两种牛奶)

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,m;
char s[N];
int f[N];
int ans[N];
//=====================================================
inline int read(){
	int p=0,f=1;
	char c;
	while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
	return p*f;
}
//======================================================
int gf(int x){
	if(x == f[x]) return x;
	return f[x] = gf(f[x]);
}
//======================================================
void hb(int x, int y){
	f[gf(x)] = gf(y);
}
//======================================================
int main(){
	n=read(),m=read();
	scanf("%s",s+1);
	int x,y;
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		if(s[x]==s[y])
			hb(x,y);
	}
	for(int i=1;i<=m;i++){
		char c;
		x=read(),y=read(),cin>>c;
		if(gf(x)==gf(y)&&s[x]!=c)ans[i]=0;
		else ans[i]=1;
	}
	for(int i=1;i<=m;i++)cout<<ans[i];
	return 0;
}

T3\\(\\color{red}0\\)题目

第二题都没做出来自然第三题也做不出来

题意:

求一颗树\\(u\\)\\(v\\)的路径上有无某颜色的点

先跑一遍dfs,再求出\\(u\\)\\(v\\)的最近公共祖先,这样就可以得到\\(u\\)\\(v\\)的路径,然后用结构体和vector存储查询的颜色,最近公共祖先以及该点的编号,

最后再来一遍dfs,这一遍相当于把颜色按照节点的深度进行赋值,然后枚举点因为刚才记录了该点所要查询的颜色以及最近公共祖先,而且\\(u\\)\\(v\\)都记录了,所以如果该点颜色深度大于最近公共祖先的深度,就说明一定能够经过,就把\\(ans[now.id]=1\\)

第三题代码复杂度\\(O\\)\\(n\\log n\\)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100,M = 2e5 + 100;
const int maxdep=24;
int n,m;
int first[M],to[M],nex[M],tot;
int val[N],dep[N],faz[N][maxdep+1],ans[N];
struct mpair{
	int col,top,id;
};
vector<mpair> vec[N];
//==============================================================
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
	return p*f;
}
//==============================================================
inline void add(int x,int y){
	nex[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
//==============================================================
void dfs1(int u){
	for(int i=1;i<=maxdep;i++)faz[u][i]=faz[faz[u][i-1]][i-1];
	for(int i=first[u];i;i=nex[i]){
		int v=to[i];
		if(v==faz[u][0])continue;
		dep[v]=dep[u]+1;
		faz[v][0]=u;
		dfs1(v);
	}
}
//==============================================================
inline int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=maxdep;i>=0;i--)
		if(dep[faz[x][i]]>=dep[y])x=faz[x][i];
	if(x==y)return x;
	for(int i=maxdep;i>=0;i--)
		if(faz[x][i]!=faz[y][i])x=faz[x][i],y=faz[y][i];
	return faz[x][0];
}
//==============================================================
int tmp[N];
void dfs(int u){
	int lst=tmp[val[u]];
	tmp[val[u]]=dep[u];
	for(int i=0;i<vec[u].size();i++){
		mpair now=vec[u][i];
		if(tmp[now.col]>=dep[now.top])
			ans[now.id]=1;
	}
	for(int i=first[u];i;i=nex[i]){
		int v=to[i];
		if(v==faz[u][0])continue;
		dfs(v);
	}
	tmp[val[u]]=lst;
}
//==============================================================
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)val[i]=read();
	int x,y;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	dfs1(1);
	int c;
	for(int i=1;i<=m;i++){
		x=read(),y=read(),c=read();
		int l=lca(x,y);
		vec[x].push_back(mpair{c,l,i});
		vec[y].push_back(mpair{c,l,i});
	}
	memset(tmp,-1,sizeof(tmp));
	dfs(1);
	for(int i=1;i<=m;i++)cout<<ans[i];
	return 0;
}

今天就是集训的最后一天了,明天就是NOIP2021了,祝所有的OIer rp++

以上是关于noip模拟测试83的主要内容,如果未能解决你的问题,请参考以下文章

csp-s模拟测试83(集训过半)

2019.5.25 Noip模拟测试2

2019.5.18 Noip模拟测试

noip模拟测试21

[noip模拟2017.7.3]

noip模拟测试9