zoukankan      html  css  js  c++  java
  • noip模拟测试83

    考试过程:这次考试,总体来说难度是不大的,我觉得前两题是可做的,首先是第一题,我觉得是个线段树板子题,也没多想,就是数据范围有点大,但是我没什么好方法优化,就打了个线段树走了。
    然后是T2,首先想出了(o(n imes log(n)))求出以1为根的答案,然后考虑移动。不难发现,当根节点移动一次时,答案相对于上一次的变化量由两部分组成:
    1.当前点为根的子树内的点的(w)小于上一次的根节点的(w)的数量。
    2.除去当前点为根的子树,剩下的点中(w)小于当前根节点的数量。
    但是我在考场上想复杂了,这是我经过优化之后的想法,当时的做法还要考虑每个子树对当前点的贡献,比较复杂,打了个树套树,但是卡在最后一个问题没有解决。就是如何在总复杂度在(O(n imes 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
    ",ans);
    	return 0;
    }
    
    
    

    T2 时代的眼泪

    思路:首先想出了(o(n imes log(n)))求出以1为根的答案,然后考虑移动。不难发现,当根节点移动一次时,答案相对于上一次的变化量由两部分组成:
    1.当前点为根的子树内的点的(w)小于上一次的根节点的(w)的数量。
    2.除去当前点为根的子树,剩下的点中(w)小于当前根节点的数量。
    但是我在考场上想复杂了,这是我经过优化之后的想法,当时的做法还要考虑每个子树对当前点的贡献,比较复杂,打了个树套树,但是卡在最后一个问题没有解决。就是如何在总复杂度在(O(n imes 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
    ",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
    ",(ans.a[4][1]+ans.a[4][2]+ans.a[4][3])%mo);
    		}
    	}
    	return 0;
    }
    
    

    T4 铺设道路

    image
    注意是差分的过程,所以要算(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
    ",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
    %lld
    ",mn,mx);
    	return 0;
    }
    
    
  • 相关阅读:
    Windows Phone 7 开发之检查手机网络
    还原ipa里的png图片资源
    phpmyadmin 自动登录的办法
    Composer 是什么
    vuejs 和 element 搭建的一个后台管理界面
    Vue2 后台管理系统解决方案
    解决无限 This file is indented with tabs instead of 4 spaces
    无聊的小知识 规格严格
    va_list!!! 规格严格
    Java动态编译一个简单的例子(我转载的,但是经过修定,可以在Eclipse下运行) 规格严格
  • 原文地址:https://www.cnblogs.com/WindZR/p/15468028.html
Copyright © 2011-2022 走看看