1. 程式人生 > >【HHHOJ】NOIP模擬賽 捌 解題報告

【HHHOJ】NOIP模擬賽 捌 解題報告

得分: 30+30+70=13030+30+70=130(弱爆了)

排名: Rank22Rank22

Rating31:Rating:-31

T1T1:【HHHOJ260】「NOIP模擬賽 捌」Digits(點此看題面

比賽時寫數位DPDP寫掛了,最後交了個裸暴力。(後來發現寫掛是因為沒考慮借位的情況)

好吧,其實數位DPDP也是可以過的,但是,好像有個更簡單的方法。

LinkLink

對於每一位,我們可以直接枚舉出相加與這一位上數字相等的兩個數字(總共只有44種情況),然後求解即可。

分類討論這裡就省略了,直接看程式碼吧:

#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 MOD 1000000007
#define Calc1(x,y) (((i-(x)-(y))/10+1)%MOD*(tot+1)%MOD*(x)%MOD*(y)%MOD)
#define Calc2(x,y) (((i-(x)-(y))/10+1)%MOD*(tn-tot-1)%MOD*(x)%MOD*(y)%MOD)
using namespace std;
LL n;
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))
		LL f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
	public:
		FIO() {FinNow=FinEnd=Fin;}
		inline void read(LL &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(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(LL 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 LL i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
int main()
{
    register LL i,j,T,ans,tn,tot,lim;F.read(T);
    while(T--)
    {
    	for(F.read(n),ans=tot=0,tn=1,i=n;i;Inc(tot,tn*(i%10)),i/=10,tn=(tn<<3)+(tn<<1))
    	{
    		if(i%10<=i) for(j=1;j<=i%10;++j) Inc(ans,Calc1(j,i%10-j));//相加與這一位相等
			if(i%10-1<=i) for(j=1;j<i%10;++j) Inc(ans,Calc2(j,i%10-j-1));//相加比這一位少1,即下一位向上進位了
    		if(i%10+10<=i) for(j=i%10+1;j<=9;++j) Inc(ans,Calc1(j,i%10-j+10));//相加比這一位多10,即向上進一位
    		if(i%10+9<=i) for(j=max(i%10,1);j<=9;++j) Inc(ans,Calc2(j,i%10-j+9));//相加比這一位多9,即下一位和這一位都向上進了一位
    	}
    	F.write(ans),F.write_char('\n');//輸出答案
	}
    return F.end(),0;
}

T2T2:【HHHOJ261】「NOIP模擬賽 捌」Brew(點此看題面

這題就比較噁心了,考試時寫了一個 暴力DPDP 交上去3030分。

後來得知要用 WQSWQS二分 +斜率優化DPDP

應該不難發現,造的釀酒廠數量越多,答案肯定越優。

但是,如果我們給造一座釀酒廠加上一個代價CostCost,我們就可以發現此時的影象應該是一個單谷函式,因此就可以用斜率優化DPDP來求解出此時的最優答案以及最優答案對應的造釀酒廠的個數

此時應該就不難想到 WQSWQS二分 CostCost,求出 造釀酒廠個數恰好為KK 時的最優答案。

程式碼如下:

#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 1e18
#define Inc(x,y) ((x+=y)>=MOD&&(x-=MOD))
#define N 100000
#define GetCost(l,r) (f[l]+GetSum((l)+1,r)+Cost)
#define Sum(l,r) (sum[r]-sum[(l)-1])
using namespace std;
LL n,m,Cost,a[N+5],sum[N+5],f[N+5],g[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))
		LL f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
	public:
		FIO() {FinNow=FinEnd=Fin;}
		inline void read(LL &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(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(LL 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 LL i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
inline LL GetSum(LL l,LL r)
{
	if(l>r) return 0;
	register LL mid=l+r>>1,ln=mid-l+1,rn=r-mid+1;
	return (Sum(mid,r)-rn*a[mid])+(ln*a[mid]-Sum(l,mid));
}
class Class_Monotone_queue//單調佇列
{
	private:
		struct key
		{
			LL S,L,R;//L和R記錄區間,S記錄上次的轉移點
			key(LL x=0,LL y=0,LL z=0):S(x),L(y),R(z){}
		}data[N+5];
		LL H,T;
	public:
		inline void Clear() {data[H=T=1]=key(0,1,n);}//清空
		inline bool empty() {return H>T;}//判斷佇列是否為空
		inline key Front() {return data[H];}//返回隊首
		inline key Back() {return data[T];}//返回隊尾
		inline void PushBack(key x) {data[++T]=x;}//在隊尾加入一個元素
		inline void PopFront() {++H;}//彈出隊首
		inline void PopBack() {--T;}//彈出隊尾
		inline void Push(LL x)//加入一個新的元素
		{
			register LL lst=-1,l,r,mid;
			while(!empty())//只要佇列不為空
			{
				if(GetCost(data[T].S,data[T].L)>GetCost(x,data[T].L)) {lst=data[T].L,PopBack();continue;}//如果原先的斜率大於當前的斜率,就彈出隊尾,並跳過當前迴圈
				for(mid=(l=data[T].L)+(r=data[T].R)>>1;l<=r;mid=l+r>>1) GetCost(data[T].S,mid)>GetCost(x,mid)?r=mid-1:l=mid+1;//二分求出最早的從當前狀態轉移要優於原先狀態轉移的時刻
				if(l<=data[T].R) data[T].R=(lst=l)-1;//更新
				break;//退出迴圈
			}
			if(~lst) PushBack(key(x,lst,n));//如果有值,將其加入佇列
			if(!empty()&&++data[H].L>data[H].R) PopFront();//如果隊首所表示區間為空,則將其彈出					
		}
}q;
inline bool check(LL C)//求出額外代價為C時的最優答案以及對應造釀酒廠的個數是否小於等於m
{
	Cost=C,q.Clear();//清空陣列
	for(register LL i=1;i<=n;++i)//斜率優化DP 
		f[i]=GetCost(q.Front().S,i),g[i]=g[q.Front().S]+1,q.Push(i);//計算出最優答案以及對應造釀酒廠的個數,然後將當前的i加入單調佇列
	return g[n]<=m;//如果g[n]≤m則返回true
}
int main()
{
    register LL i,j,l,r,mid;
    for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]);
    for(sort(a+1,a+n+1),i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
    for(mid=(l=0)+(r=INF)>>1;l<=r;mid=l+r>>1) check(mid)?r=mid-1:l=mid+1;//WQS二分
    return check(l),F.write(f[n]-l*m),F.end(),0;//輸出答案
}

T3T3:【HHHOJ262】「NOIP模擬賽 捌」QTree(點此看題面

先吐槽一波比賽時的資料太水(雖然我懶得優化暴力,結果還是沒過)。

現在資料加強了,我本來改完能過的暴力程式碼現在被一個類似於菊花圖的東西給卡崩了。

暴力我覺得就不用多講了,大不了就是直接暴力修改+詢問。

唯一要注意的地方應該是關於visvis陣列的清空,我比較建議記錄一下每次操作的編號,這樣就可以不用清空陣列,起到了極大程度的優化。

還是談一談正解吧(雖然我還沒過),正解的大致思路是將節點的BFSBFS序存下來,然後用一棵線段樹維護。

聽起來好像並不難的樣子。

但是,考慮到這是一棵基環外向樹聽說無論什麼題目一套上一個基環外向樹就會碼量大增… …

看了一下ACAC程式碼,4.0kb4.0kb起步,頓時失去了打的勇氣… …

程式碼以後再補吧。