zoukankan      html  css  js  c++  java
  • 2019.03.19 ZJOI2019模拟赛 解题报告

    得分: (100+10+45=155)(T1)又是水题,(T2)写暴力,(T3)大力(STL)乱搞)

    (T1):哈夫曼树

    首先,根据题目中给出的式子,可以发现,我们要求的其实就是每种方案下的总代价和。

    显然,每个数被选择的总次数应该是相同的。

    因此,我们可以设(f_i)在还剩下(i)个数时进行操作后,每个数被选择的次数

    则易推得转移方程:

    [f_i=frac{i(i-1)}2*f_{i+1}+(i-1)prod_{j=i+1}^nfrac{j(j-1)}2 ]

    对于这个式子,我们可以这样考虑:

    • 由于每一次合并时每个数被选择的次数是可以独立计算的,所以,我们可以直接通过(frac{i(i-1)}2*f_{i+1})这个式子来计算之前的总贡献。(即上一步每个数被选择的次数从上一步到这一步的转移方案数
    • 而当前,这个数被选择的方案数为(i-1),即选择包含这个数的位置和其余(i-1)个位置中的任意一个位置合并。由于还剩(i)个数的方案数共有(prod_{j=i+1}^nfrac{j(j-1)}2)种,因此当前的贡献就是((i-1)prod_{j=i+1}^nfrac{j(j-1)}2)

    还有一点要注意的,就是最后总方案数为(f_2)

    然后将(f_2)乘上数的总和,就是答案了。

    代码如下:

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define X 1000000007
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    #define Dec(x,y) ((x-=(y))<0&&(x+=X))
    #define XSum(x,y) ((x)+(y)>=X?(x)+(y)-X:(x)+(y))
    using namespace std;
    int n,a[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class DpSolver
    {
    	private:
    		#define S(x) ((1LL*(x)*((x)-1)>>1)%X)
    		int f[N+5];
    	public:
    		I int GetAns()
    		{
    			RI i,j,t=1,sum=0;for(i=n;i^1;--i)//枚举状态进行转移
    				f[i]=XSum(1LL*S(i)*f[i+1]%X,1LL*(i-1)*t%X),t=1LL*S(i)*t%X;
    			for(i=1;i<=n;++i) Inc(sum,a[i]);return 1LL*f[2]*sum%X;//输出答案
    		}
    }DP;
    int main()
    {
    	freopen("huffman.in","r",stdin),freopen("huffman.out","w",stdout);
    	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i]);return printf("%d",DP.GetAns()),0;
    }
    

    (T2):线段树

    首先,可以发现一个比较显然的性质:最后查询的这个数的值肯定是一段连续区间的最大值

    则我们所要做的,就是求出这段区间,然后最大值就很好求了。

    考虑对当前询问的数,我们找到在它之前最靠近它且包含它的一个区间,然后再去寻找与这个区间有重叠的其他区间,最后就能扩展成一个很大的区间。

    那我们能不能从前往后正着进行一遍这样的过程呢?

    显然可以。

    我们可以定义(f1_i)(f2_i),分别表示满足(j<i)(L_jle L_ile R_j)的最大(j)满足(j<i)(L_jle R_ile R_j)的最大(j)

    这就相当于分别求出最靠近该区间且与该区间有重叠的左边和右边的区间。

    这一过程可以直接用线段树,但因为后面的某些特殊需求,因此需要主席树

    则不难发现,对于第(i)个区间,它的答案会受到(f1_i)(f2_i)两个区间的影响,而(f1_i)(f2_i)又会受到各自的(f1)(f2)的影响。

    如果我们考虑吧(f1_i)(f2_i)分别看作(i)的父亲,就可以建出两棵树。

    而对于一次询问,我们就可以像前面说的那样,在([l,r])范围内找到在它之前最靠近它且包含它的一个区间(这里就需要使用主席树了),然后在两棵树中分别倍增向上跳,分别找到最远的且大于等于(l)的操作,然后就能确定出会影响当前位置的区间范围了。

    再就相当于线段树上询问区间最大值了。

    代码如下:

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define Log 20
    #define max(x,y) ((x)>(y)?(x):(y))
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    using namespace std;
    int n,m,a[N+5],b[N+5],ql[N+5],qr[N+5],f1[N+5][Log+5],f2[N+5][Log+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void writeln(Con Ty& x) {write(x),pc('
    ');}
    		I void clear() {fwrite(FO,1,C,stdout),C=0;}
    }F;
    class ChairmanTree//主席树
    {
    	private:
    		#define STO l,hl,O[rt].S[0]
    		#define ORZ hl+1,r,O[rt].S[1]
    		#define PD(x) (O[x].S[0]&&Gmax(O[O[x].S[0]].V,O[x].V),O[x].S[1]&&Gmax(O[O[x].S[1]].V,O[x].V))
    		int v,tot,Rt[N+5];struct Il {int V,S[2];}O[N*Log<<2];
    		I void ins(CI l,CI r,int& rt,CI lst,CI ul,CI ur,CI w)//插入新节点,区间修改,维护最靠近某个节点且包含它的一个区间
    		{
    			if(O[rt=++tot]=O[lst],ul<=l&&r<=ur) return 
    				(void)(Gmax(O[rt].V,w),O[rt].S[0]=++tot,O[rt].S[1]=++tot);
    			PD(rt);RI hl=l+r>>1;ul<=hl&&(ins(STO,O[lst].S[0],ul,ur,w),0),
    			ur>hl&&(ins(ORZ,O[lst].S[1],ul,ur,w),0);
    		}
    		I int qry(CI l,CI r,CI rt,CI x)//询问
    		{
    			if(!x||!(l^r)) return O[rt].V;PD(rt);RI hl=l+r>>1,res;
    			return res=(x<=hl?qry(STO,x):qry(ORZ,x)),max(O[rt].V,res);
    		}
    	public:
    		I void Insert(CI l,CI r,CI w) {++v,ins(1,n,Rt[v],Rt[v-1],l,r,w);}
    		I int Query(CI t,CI x) {return qry(1,n,Rt[t],x);}
    }C;
    class SegmentTree//线段树
    {
    	private:
    		#define STO l,hl,rt<<1
    		#define ORZ hl+1,r,rt<<1|1
    		#define PU(x) (V[x]=max(V[x<<1],V[x<<1|1]))
    		int n,v[N+5],V[N<<2];
    		I void Build(CI l,CI r,CI rt)//初始化建树
    		{
    			if(!(l^r)) return (void)(V[rt]=v[l]);RI hl=l+r>>1;
    			Build(STO),Build(ORZ),PU(rt);
    		}
    		I void upt(RI l,RI r,RI rt,CI x,CI y)//单点修改,这里用了非递归实现
    		{
    			RI hl;W(l^r) hl=l+r>>1,x<=hl?(r=hl,rt<<=1):(l=hl+1,(rt<<=1)|=1);
    			V[rt]=y;W(rt>>=1) PU(rt);
    		}
    		I int qry(CI l,CI r,CI rt,CI ql,CI qr)//区间求最大值
    		{
    			if(ql<=l&&r<=qr) return V[rt];RI hl=l+r>>1,res=0,t;
    			return ql<=hl&&(t=qry(STO,ql,qr),Gmax(res,t)),qr>hl&&(t=qry(ORZ,ql,qr),Gmax(res,t)),res;
    		}
    	public:
    		I void Init(CI x,int* s) {for(RI i=1;i<=x;++i) v[i]=s[i];Build(1,n=x,1);}
    		I void Update(CI x,CI y) {upt(1,n,1,x,y);}
    		I int Query(CI x,CI y) {return qry(1,n,1,x,y);}
    }S;
    I void Pre()//初始化
    {
    	RI i,j;for(i=1;i<=m;++i) f1[i][0]=C.Query(i-1,ql[i]),//扫一遍,初始化主席树,同时求出f1与f2
    		f2[i][0]=C.Query(i-1,qr[i]),C.Insert(ql[i],qr[i],i);
    	for(j=1;j<=Log;++j) for(i=1;i<=n;++i)//预处理倍增数组
    		f1[i][j]=f1[f1[i][j-1]][j-1],f2[i][j]=f2[f2[i][j-1]][j-1];
    }
    int main()
    {
    	freopen("segment.in","r",stdin),freopen("segment.out","w",stdout);
    	RI Qtot,i,op,x,y,z,fl,fr;for(F.read(n,m,Qtot),i=1;i<=n;++i) F.read(a[i]);//读入数据
    	for(i=1;i<=m;++i) F.read(ql[i],qr[i]);Pre(),S.Init(n,a);W(Qtot--)//读入数据,预处理
    	{
    		if(F.read(op,x,y),op^2) {a[x]=y,S.Update(x,y);continue;}//单点修改
    		if(F.read(z),(fl=fr=C.Query(y,z))<x) {F.writeln(a[z]);continue;}//求出最靠近当前点且包含当前点的区间,若编号小于x,则直接输出这一位上的值
    		for(i=Log;~i;--i) f1[fl][i]>=x&&(fl=f1[fl][i]),f2[fr][i]>=x&&(fr=f2[fr][i]);//倍增向上跳,求出对当前位置有影响的区间
    		F.writeln(S.Query(ql[fl],qr[fr]));//输出答案
    	}return F.clear(),0;
    }
    

    (T3):接水问题

    (STL)大暴力,水了(45)分。

    不会订正,待补。

  • 相关阅读:
    Mac终端运行java程序
    Mac上csv导入mysql提示错误[Error Code] 1290
    Mac终端使用mysql
    将spark默认日志log4j替换为logback
    Cocos2d-x 3.x部署到安卓
    Cocos2d-x 3.0 场景切换
    visual studio 未将对象引用设置到对象的实例
    C++ list用法
    减而治之
    递归
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Contest20190319.html
Copyright © 2011-2022 走看看