zoukankan      html  css  js  c++  java
  • test20190814 NOIP2019 模拟题

    二叉树

    【问题描述】
    从前有一棵二叉树,我们用如下方式来表示这棵二叉树。

    1. 如果一个节点没有儿子,我们用“0”来表示他。
    2. 如果一个节点有一个儿子,我们对它的表示以“1”开头,后面接对它儿子的表示。
    3. 如果一个节点有两个儿子,我们对它的表示以“2”开头,后面先接对它左儿子的表示,后接对它右儿子的表示。

    KJDH 十分贪玩,将这棵树染了色,KJDH 又十分聪明,它染色又很有规则:每个节点不能和它的孩子有相同的颜色,如果一个节点有两个孩子,那么这两个孩子也不能有相同的颜色。 由于这个树年代久远了,所以我们看不清每个节点的颜色了,但我们知道KJDH 只染了红黄白三种颜色。我们想知道这棵树最多和最少有多少个节点是白色的。

    【输入格式】
    输入文件名为 tree.in。 输入文件只有一行,一个字符串,只有“0”,“1”,“2”组成,表示这 棵树的结构。

    【输出格式】
    输出文件名为 tree.out。 输出文件包含两个用空格隔开的数,分别表示白色节点的最多和最少数量。

    【输入输出样例 1】
    tree.in
    200
    tree.out
    1 1

    【输入输出样例 2】
    tree.in
    1122002010
    tree.out
    5 2

    【数据规模与约定】
    对于 20% 的数据,len<=10。
    对于 50% 的数据,len<=2000
    对于 100% 的数据,len<=500000。其中 len 为读入字符串的长度。

    题解

    我还想了好一会儿怎么建树。用栈模拟括号序列即可。

    然后就是睿智树形DP。

    co int N=500000+10;
    char str[N];
    struct node{int id,val;};
    int len,lc[N],rc[N],n;
    int main(){
    	freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
    	scanf("%s",str+1),len=strlen(str+1);
    	stack<node> st;
    	for(int i=1;i<=len;++i){
    		st.push((node){++n,str[i]-'0'});
    		while(st.top().val==0){
    			int x=st.top().id;st.pop();
    			if(x==1) break;
    			int fa=st.top().id;
    //			cerr<<"link "<<fa<<" "<<x<<endl;
    			lc[fa]?rc[fa]=x:lc[fa]=x;
    			--st.top().val;
    		}
    	}
    	dfs(1);
    	printf("%d %d
    ",max(f[1][0],max(f[1][1],f[1][2])),min(g[1][0],min(g[1][1],g[1][2])));
    	return 0;
    }
    

    跳舞

    【问题描述】
    KJDH 有 n 个妹子,从 1 到 n 依次编号,每个妹子都会跳舞,第 i 个妹子跳舞的魅力值为 ai,有一天 KJDH 在 IOI 赛场上捧了杯,他的 n 个妹子想要庆祝一下,要为他跳舞,总共要跳 n*(n+1)/2 支舞,分别由编号为 i~j 的妹子跳舞(1<=i<=j<=n)。

    每跳一支舞 KJDH 都会非常高兴从而增加愉悦值,编号为 i~j 的妹子跳舞能增加的愉悦值为
    (j-i+1)Min(ai,ai+1,…,aj)Max(ai,ai+1,…,aj)

    问 KJDH 在跳完 n*(n+1)/2 支舞后,能增加多少愉悦值,答案对 1000000007 取模。

    【输入格式】
    输入文件名为 dance.in。
    输入共 2 行。
    第 1 行包含 1 个正整数 n ,表示 n 个妹子。
    第 2 行包含 n 个用空格隔开的正整数 a1,a2,…,an。表示每个妹子跳舞的魅力值。

    【输出格式】
    输出文件名为 dance.out。
    输出共 1 行,包含 1 个整数,表示 KJDH 能增加的愉悦值。

    【输入输出样例 1】
    dance.in
    4
    2 4 1 4
    dance.out
    109

    【输入输出样例 1 说明】
    总共跳了 6 支舞。用(i,j)表示编号 i~j 的妹子跳舞增加的愉悦值。
    (1,1)=4 (1,2)=16 (1,3)=12 (1,4)=16
    (2,2)=16 (2,3)=8 (2,4)=12
    (3,3)=1 (3,4)=8
    (4,4)=16
    全部加起来为 109。

    【数据规模与约定】
    对于 60%的数据,n<=2000;
    对于 100%的数据,n<=500000,1<=ai<=108

    ChiTongZ的题解

    (j-i+1)从答案中拆出去

    [sum_{j=1}^nsum_{i=1}^j(j-i+1) max(i,j) min(i,j)\ =sum_{j=1}^n j sum_{i=1}^j max(i,j) min(i,j) - sum_{i=1}^n (i-1) sum_{j=i}^n max(i,j) min(i,j) ]

    第二重求和符号可以用单调栈+线段树维护。

    用单调栈来得知区间覆盖情况,用线段树维护区间覆盖标记,区间min、max和,区间min*max和。发现就可以做了。

    时间复杂度(O(n log n)),出题人卡常数。

    #include<bits/stdc++.h>
    #define co const
    #define il inline
    template<class T> T read(){
    	T x=0,w=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*w;
    }
    template<class T> T read(T&x){
    	return x=read<T>();
    }
    using namespace std;
    typedef long long LL;
    
    co int mod=1000000000+7;
    il int add(int a,int b){
    	return (a+=b)>=mod?a-mod:a;
    }
    il int mul(int a,int b){
    	return (LL)a*b%mod;
    }
    
    co int N=500000+10;
    namespace T{
    	int l[N<<2],r[N<<2];
    	int tag[N<<2][2],sum[N<<2][2];
    	int pro[N<<2];
    	#define lc (x<<1)
    	#define rc (x<<1|1)
    	void build(int x,int l,int r){
    		T::l[x]=l,T::r[x]=r;
    		tag[x][0]=tag[x][1]=sum[x][0]=sum[x][1]=0;
    		pro[x]=0;
    		if(l==r) return;
    		int mid=(l+r)>>1;
    		build(lc,l,mid),build(rc,mid+1,r);
    	}
    	void set(int x,int k,int v){
    		tag[x][k]=v,sum[x][k]=mul(r[x]-l[x]+1,v);
    		pro[x]=mul(sum[x][k^1],v);
    	}
    	void push_down(int x){
    		for(int k=0;k<2;++k)if(tag[x][k]){
    			set(lc,k,tag[x][k]),set(rc,k,tag[x][k]);
    			tag[x][k]=0;
    		}
    	}
    	void push_up(int x){
    		for(int k=0;k<2;++k)
    			sum[x][k]=add(sum[lc][k],sum[rc][k]);
    		pro[x]=add(pro[lc],pro[rc]);
    	}
    	void change(int x,int ql,int qr,int k,int v){
    		if(ql<=l[x]&&r[x]<=qr)
    			return set(x,k,v);
    		push_down(x);
    		int mid=(l[x]+r[x])>>1;
    		if(ql<=mid) change(lc,ql,qr,k,v);
    		if(qr>mid) change(rc,ql,qr,k,v);
    		push_up(x);
    	}
    	int query(int x,int ql,int qr){
    		if(ql<=l[x]&&r[x]<=qr)
    			return pro[x];
    		push_down(x);
    		int mid=(l[x]+r[x])>>1;
    		if(qr<=mid) return query(lc,ql,qr);
    		if(ql>mid) return query(rc,ql,qr);
    		return add(query(lc,ql,qr),query(rc,ql,qr));
    	}
    }
    int n,a[N];
    int s1[N],t1,s2[N],t2; // max,min
    
    int main(){
    	freopen("dance.in","r",stdin),freopen("dance.out","w",stdout);
    	read(n);
    	for(int i=1;i<=n;++i) read(a[i]);
    	int ans=0;
    	T::build(1,1,n);
    	for(int i=1;i<=n;++i){
    		while(t1&&a[s1[t1]]<=a[i]) --t1;
    		T::change(1,t1?s1[t1]+1:1,i,1,a[i]);
    		s1[++t1]=i;
    		while(t2&&a[s2[t2]]>=a[i]) --t2;
    		T::change(1,t2?s2[t2]+1:1,i,0,a[i]);
    		s2[++t2]=i;
    		ans=add(ans,mul(i,T::query(1,1,i)));
    	}
    	T::build(1,1,n),t1=t2=0;
    	for(int i=n;i>=1;--i){
    		while(t1&&a[s1[t1]]<=a[i]) --t1;
    		T::change(1,i,t1?s1[t1]-1:n,1,a[i]);
    		s1[++t1]=i;
    		while(t2&&a[s2[t2]]>=a[i]) --t2;
    		T::change(1,i,t2?s2[t2]-1:n,0,a[i]);
    		s2[++t2]=i;
    		ans=add(ans,mod-mul(i-1,T::query(1,i,n)));
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    数列

    【问题描述】
    我们定义 n-数列是具有如下性质的数列。

    1. 数列的长度不小于 3,且数列中的每个元素都是 1 到 n 之间的整数。
    2. 若数列为 a1,a2,……,am,则对于任意 3<=k<=m,都满足
      (ak-ak-2)(ak-1-ak-2)<0

    现在给你 n,求 n-数列的个数。答案对 1000000007 取模。

    【输入格式】
    输入文件名为 seq.in。
    输入共一行,为 n。

    【输出格式】
    输出文件名为 seq.out。 输出一行,表示 n-数列的个数

    【输入输出样例 1】
    seq.in
    3
    seq.out
    2

    【输入输出样例 1 说明】
    两个 n-序列分别是(2,1,3)和(2,3,1)

    【输入输出样例 2】
    seq.in
    666
    seq.out
    805846404

    【数据规模与约定】
    对于10%的数据,n<=10
    对于30%的数据,n<=200
    对于50%的数据,n<=2000
    对于70%的数据,n<=1018
    对于100%的数据,3<=n<=105000

    还是ChiTongZ的题解

    注意到若设(b_i=a_{i+1}-a_i),则(|b_i|)是单调递增的,并且(b_i)正负交替。

    考虑(a_i - i)的图像,把它连成折线图,那么我们要做的就是确定(a_i)的波动范围,即(max { |b_i| }=|b_m|),然后求出这个波动范围的方案数,乘上这个范围摆放位置的方案数。

    考虑枚举(|b_m|),先假设(b_m)为正数,最后方案数乘以(2)即可。

    [frac 12 ans=sum_{i=1}^{n-1} 2^{i-1} (n-1-i+1) ]

    因为(b)的严格的性质,所以(1 sim i-1)的任何一种出现方式都唯一对应一种方案。

    但是这里没有保证(|{a_n}| ge 3),即(|{b_n}| ge 2),所以要减去(| { b_n }| = 1)情况。

    [frac 12 ans=sum_{i=1}^{n-1} 2^{i-1} (n-1-i+1) - sum_{i=1}^{n-1} (n-1-i+1) ]

    然后通过现有的套路进行计算,得出答案

    [ans=2^{n+1}-n^2-n-2 ]

    时间复杂度(O(lg n))

    #include<bits/stdc++.h>
    #define co const
    #define il inline
    template<class T> T read(){
    	T x=0,w=1;char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*w;
    }
    template<class T> T read(T&x){
    	return x=read<T>();
    }
    using namespace std;
    typedef long long LL;
    
    co int mod=1000000000+7;
    il int fpow(int a,int b){
    	int ans=1;
    	for(;b;b>>=1,a=(LL)a*a%mod)
    		if(b&1) ans=(LL)ans*a%mod;
    	return ans;
    }
    
    int main(){
    	freopen("seq.in","r",stdin),freopen("seq.out","w",stdout);
    	int m=0,n=0;
    	for(char c=getchar();isdigit(c);c=getchar()){
    		m=((LL)m*10+c-'0')%(mod-1);
    		n=((LL)n*10+c-'0')%mod;
    	}
    	int ans=fpow(2,m+1)+mod-((LL)n*n+n+2)%mod;
    	printf("%d
    ",ans%mod);
    	return 0;
    }
    
  • 相关阅读:
    从内积的观点来看线性方程组
    《线性规划》(卢开澄,卢华明) 例2.1
    斐波那契数列
    共几只桃子
    计算 $s=1+(1+2)+(1+2+3)+cdots+(1+2+3+cdots+n)$
    【★】路由环路大总结!
    Apache与Tomcat有什么关系和区别
    Apache与Tomcat有什么关系和区别
    逻辑卷、物理卷、卷组 的关系
    逻辑卷、物理卷、卷组 的关系
  • 原文地址:https://www.cnblogs.com/autoint/p/test20190814.html
Copyright © 2011-2022 走看看