zoukankan      html  css  js  c++  java
  • 【HHHOJ】NOIP模拟赛 玖 解题报告

    点此进入比赛

    得分: (100+20+100=220)(还不错)

    排名: (Rank 16)

    (Rating)(+20)

    (T1):【HHHOJ263】「NOIP模拟赛 玖」三个和尚(点此看题面

    第一眼看完毫无想法。

    仔细思考,可以发现一个性质:只要原数数位之和(sum)不能被(3)整除,就说明无解

    然后就很简单了,只要使输出的(3)个数数位之和全部为(frac{sum}3)即可。

    代码如下:

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1)) 
    #define N 10000 
    using namespace std;
    int n=0,num[N+5];
    class FIO
    {
    	private:
    		#define Fsize 100000
    		#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    		#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
    		int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
    	public:
    		FIO() {FinNow=FinEnd=Fin;}
    		inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
    		inline bool read_digit(int &x) {while(!isdigit(x=tc())) if(!~x) return false;return x&=15,true;}
    		inline void read_char(char &x) {while(isspace(x=tc()));}
    		inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
    		inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
    		inline void write_char(char x) {pc(x);}
    		inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
    		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    inline void Solve(int x,int v)//x表示当前处理到的位数,v表示剩余的数位和
    {
    	if(num[x]>=v) return (void)(F.write_char(v+48),num[x]-=v);//如果该位数字≥v,就输出v,并将该位数字减去v
    	Solve(x-1,v-num[x]),F.write_char(num[x]+48),num[x]=0;//如果该位数字<v,就处理更高的一位,然后输出该位数字(注意,这顺序不能反),然后将该位数字赋值为0
    }
    int main()
    {
        register int i,x,sum=0;
        while(F.read_digit(x)) sum+=(num[++n]=x);//用sum统计数位之和
        if(sum%3) return puts("-1"),0;//如果sum不能被3整除,说明无解
        for(i=1;i<=3;++i) Solve(n,sum/3),F.write_char(' ');//输出答案
        return F.end(),0;
    }
    
    

    (T2):【HHHOJ264】「NOIP模拟赛 玖」疯狂祖玛(点此看题面

    比赛时写了一个区间(DP),样例水过,交上去挂了,只有(20)分... ...

    正解貌似是一个更加高级的区间(DP),但是要用记忆化搜索实现,思路十分巧妙。

    为了简便,我们可以用(NeedBall(x))表示(x)个颜色相同且连续的珠子时还需加多少个珠子才能将其消掉。具体实现如下:

    #define NeedBall(x) ((x)<k?k-(x):0)
    

    首先,我们把颜色相同且连续的珠子压缩在一起,用(col_i)表示颜色,(num_i)表示个数。

    然后,考虑用(f_{l,r,v})表示在编号为(r)的珠子右边有(v)个与(r)号珠子同色的珠子时,要把([l,r])区间内的珠子消完所需要增加的最少珠子数

    则有两种转移方式:

    • 直接加上(Need(v+num_r))个珠子把(r)号珠子消掉。即(f_{l,r,v}=f_{l,r-1,0}+Need(v+num_r))
    • 找到([l,r])范围内的一个(i)使其满足(col_i=col_r),我们可以选择将([i+1,r-1])之间的珠子消掉,然后将(i)(r)两块珠子连在一起。即(f_{l,r,v}=min(f_{l,r,v},f_{l,i,min(k,v+num_r)}+f_{i+1,r-1,0}))

    代码如下:

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1)) 
    #define N 100
    #define K 5 
    #define AddBalls(y,z) (s[++n]=Balls(y,z),lst[n]=End[y],End[y]=n)
    #define NeedBall(x) ((x)<k?k-(x):0)//求出还需多少个球
    using namespace std;
    int n,k,lst[N+5],End[N+5];
    struct Balls//记录连续一段颜色相同的求的信息
    {
    	int col,num;//col记录颜色,num记录数量
    	Balls(int x=0,int y=0):col(x),num(y){}
    }s[N+5];
    class FIO
    {
    	private:
    		#define Fsize 100000
    		#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    		#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
    		int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
    	public:
    		FIO() {FinNow=FinEnd=Fin;}
    		inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
    		inline void read_char(char &x) {while(isspace(x=tc()));}
    		inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
    		inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
    		inline void write_char(char x) {pc(x);}
    		inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
    		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_DP
    {
    	private:
    		int f[N+5][N+5][K+5];//f[l][r][v]表示在编号为r的珠子右边有v个与r号珠子同色的珠子时,要把[l,r]区间内的珠子消完所需要增加的最少珠子数
    	public:
    		inline void Init() {for(register int i=0,j,l;i<=n;++i) for(j=i;j<=n;++j) for(l=0;l<=k;++l) f[i][j][l]=-1;}//初始化全部为-1,方便记忆化
    		inline int GetAns(int l,int r,int v)//记忆化搜索
    		{
    			if(~f[l][r][v]) return f[l][r][v];//如果已经求解过,返回求出的答案
    			register int i,t;
    			for(f[l][r][v]=GetAns(l,r-1,0)+NeedBall(s[r].num+v),i=l;i<r;++i)
    				if(!(s[i].col^s[r].col)) t=GetAns(l,i,min(k,s[r].num+v))+GetAns(i+1,r-1,0),f[l][r][v]=min(f[l][r][v],t);//状态转移
    			return f[l][r][v];	
    		}
    }DP;
    int main()
    {
        register int i,nn,x,y,z=1;
        for(F.read(nn),F.read(k),F.read(y),--nn;nn;--nn) F.read(x),(x^y?(AddBalls(y,z),y=x,z=1):++z);
        return AddBalls(y,z),DP.Init(),F.write(DP.GetAns(1,n,0)),F.end(),0;//输出答案
    }
    

    (T3):【HHHOJ265】「NOIP模拟赛 玖」回文单词(点此看题面

    说实话,这题就是一个比较简单的哈希

    考虑题意,其实不难有一个贪心的想法:每次选取最短的相同的前缀和后缀,将其从字符串中删去,并将答案加(1)

    关于如何求出最短的相同的前缀和后缀,我首先想到的是利用(KMP)算法(Next)数组。

    后来发现不行(因为前缀是在变化的)。

    所以我们要用更灵活的字符串匹配方式:字符串哈希

    个人感觉得出上面那个结论,这道题就不难了。

    具体实现可以看下面的代码:

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1)) 
    #define N 1000000
    using namespace std;
    string st; 
    class FIO
    {
    	private:
    		#define Fsize 100000
    		#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    		#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
    		int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
    	public:
    		FIO() {FinNow=FinEnd=Fin;}
    		inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
    		inline void read_char(char &x) {while(isspace(x=tc()));}
    		inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
    		inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
    		inline void write_char(char x) {pc(x);}
    		inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
    		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_HashSolver//用字符串哈希求解答案
    {
    	private:
    		struct Hash//双哈希
    		{
    			ull fir,sec;
    			Hash(ull x=0,ull y=0):fir(x),sec(y){}
    		}seed,s[N+5],t[N+5];
    		inline friend Hash operator + (Hash x,Hash y) {return Hash(x.fir+y.fir,x.sec+y.sec);}//重载加法
    		inline friend Hash operator - (Hash x,Hash y) {return Hash(x.fir-y.fir,x.sec-y.sec);}//重载减法
    		inline friend Hash operator * (Hash x,Hash y) {return Hash(x.fir*y.fir,x.sec*y.sec);}//重载乘法
    		inline friend bool operator == (Hash x,Hash y) {return !(x.fir^y.fir||x.sec^y.sec);}//重载等于号
    	public:
    		Class_HashSolver() {seed=Hash(31,41),t[0]=Hash(1,1);for(register int i=1;i<=N;++i) t[i]=t[i-1]*seed;}//初始化
    		inline void Init(string st) {s[0]=Hash(1,1);for(register int i=1,len=st.length();i<=len;++i) s[i]=s[i-1]*seed+Hash(st[i-1]&31,st[i-1]&31);}//对字符串进行哈希 
    		inline bool check(int l1,int r1,int l2,int r2) {return (s[r1]-(s[l1-1]*t[r1-l1+1]))==(s[r2]-(s[l2-1]*t[r2-l2+1]));}//比较两段区间内的字符串是否相同
    		inline int GetAns(int l,int r)//求解[l,r]区间内的答案
    		{
    			if(l>r) return 0; 
    			for(register int i=l;(i<<1)<(l+r);++i) if(check(l,i,l+r-i,r)) return GetAns(i+1,l+r-i-1)+2;//找到最短的相同的前缀和后缀,将其从字符串中删去,并将答案加2 
    			return 1;//否则,这整个字符串对答案造成的贡献为1
    		}
    }HashSolver;
    int main()
    {
    	register int T;F.read(T);
    	while(T--) F.read_string(st),HashSolver.Init(st),F.write(HashSolver.GetAns(1,st.length())),F.write_char('
    ');
    	return F.end(),0;
    }
    
  • 相关阅读:
    win10安装Redis
    imemode属性解释
    DataTable导出到Excel
    Oracle中wm_concat函数报错解决方法
    GridView中生成复合表头
    WinXP下无法打开ACCESS解决办法
    提醒功能
    网页常用Javascript
    ASP.NET记录错误日志
    GridView中Js的应用
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/HHHOJ_NOIP2018_nine.html
Copyright © 2011-2022 走看看