1. 程式人生 > 其它 >NOIP模擬51

NOIP模擬51

「茅山道術·泰拳警告·萬豬拱塔·抑鬱刀法」on 9.12

櫻花滿地集於我心,楪舞紛飛祈願相隨

前言

太菜了,人手切掉兩個題,我竟然一道都不會。。

改 T3 的時候整個人的心態都崩掉了,一部分原因可能是語文素養不高導致我看不懂題解。

另一部分可能就是系太不太好,受不了打擊。。。(又菜又玩不起

後來稍微又看了一下題才發現自己 T3 少看了 w 互不相同這個條件,難怪感覺題解越想越不對。。

T1 茅山道術

解題思路

動態規劃

\(f_i\) 表示前 \(i\) 個的方案數,狀態可以由 \(i-1\) 或者 \(i\) 位置的這個顏色的上一個出現位置轉移過來,正確性顯然。

假設位置 \(i\) 顏色上一次出現位置為 \(las_i\)\(f_i=f_{i-1}+f_{las_i}\times[las_i\ne i-1]\)

,邊界是 \(f_0=1\)

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e6+10,M=2e3+10,mod=1e9+7;
int n,ans,s[N],las[N],bef[N],f[N];
signed main()
{
	freopen("magic.in","r",stdin);
	freopen("magic.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) s[i]=read();
	for(int i=1;i<=n;i++)
	{
		las[i]=bef[s[i]];
		bef[s[i]]=i;
	}
	f[0]=1;
	for(int i=1;i<=n;i++)
	{
		int j=las[i];
		f[i]=f[i-1];
		if(j) f[i]=(f[i]+f[j]*(j<i-1))%mod;
	}
	printf("%lld",f[n]);
	return 0;
}

T2 泰拳警告

解題思路

列舉輸贏局總數進行計算。

假設輸贏局總數是 \(i\) 那麼贏的局數一定要多於一半,對於一局我們把平局的方案數看作是 \(p\) 種,輸贏的情況數各為 \(1\) 種,最後的答案除去 \((p+2)^n\) 就好了。

那麼可以像官方題解一樣看作 \((x+x^{-1})^i\) 次冪大於 \(0\) 其實也就是贏的局數大於輸的局數,然後二項式展開。

通俗理解的話就是從 \(i\) 場中最少贏 \(\lfloor\dfrac{i+1}{2}\rfloor\) 場才算合法的情況,那麼方案數也就是 \(t(i)=\sum\limits_{j=0}^iC_i^j\times[j\ge\lfloor\dfrac{i+1}{2}\rfloor]\)

對於偶數奇數分別計算,顯然 \(i\) 為奇數的時候 \(t(i)=\dfrac{\sum\limits_{j=0}^i C_j^j}{2}\) 也就是 \(t(i)=2^{i-1}\)

對於偶數的時候只需要去掉中間的一項就好了 \(t(i)=2^{i-1}-\dfrac{C^{\frac{i}{2}}_{i}}{2}\)

然後對於剩下的平局就有 \(p^{n-i}\) 種方案,對於 \(n\) 局進行排列,並且去掉等效的局數內部的排序,因此我們需要在乘上 \(\dfrac{A_n^n}{A_i^i\times A_{n-i}^{n-i}}\)

code

一般觀賞版



#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=3e6+10,mod=998244353;
int n,p,ans,fac[N],ifac[N];
int power(int x,int y)
{
	int temp=1;
	while(y)
	{
		if(y&1) temp=temp*x%mod;
		x=x*x%mod; y>>=1;
	}
	return temp;
}
int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
int A(int x,int y){return fac[x]*ifac[x-y]%mod;}
signed main()
{
	freopen("fight.in","r",stdin);
	freopen("fight.out","w",stdout);
	n=read(); p=read(); fac[0]=ifac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	ifac[n]=power(fac[n],mod-2);
	for(int i=n-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
	for(int i=1;i<=n;i++)
	{
		int pre=ans;
		if(i&1) ans=(ans+power(2,i-1)*(n-i+1)%mod*power(p,n-i)%mod*fac[n]%mod*ifac[i]%mod*ifac[n-i])%mod;
		else ans=(ans+(power(2,i-1)-C(i,i/2)*power(2,mod-2)%mod+mod)%mod*(n-i+1)%mod*power(p,n-i)%mod*fac[n]%mod*ifac[i]%mod*ifac[n-i])%mod;
	}
	printf("%lld",ans*power(power(p+2,n),mod-2)%mod);
	return 0;
}

極致壓行版

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=3e6+10,mod=998244353;
int n,p,ans,inv2,invp,fac[N],ifac[N];
int power(int x,int y){int temp=1;while(y){if(y&1) temp=temp*x%mod;x=x*x%mod; y>>=1;}return temp;}
int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
int A(int x,int y){return fac[x]*ifac[x-y]%mod;}
signed main(){
	freopen("fight.in","r",stdin); freopen("fight.out","w",stdout);
	n=read(); p=read(); inv2=power(2,mod-2); invp=power(p,mod-2);
	fac[0]=ifac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	ifac[n]=power(fac[n],mod-2); for(int i=n-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
	for(int i=1,base=1,basep=power(p,n-1);i<=n;i++,base=base*2%mod,basep=basep*invp%mod)
		if(i&1) ans=(ans+base*(n-i+1)%mod*basep%mod*fac[n]%mod*ifac[i]%mod*ifac[n-i])%mod;
		else ans=(ans+(base-C(i,i/2)*inv2%mod+mod)%mod*(n-i+1)%mod*basep%mod*fac[n]%mod*ifac[i]%mod*ifac[n-i])%mod;
	printf("%lld",ans*power(power(p+2,n),mod-2)%mod);
	return 0;
}

T3 萬豬拱塔

解題思路

非常非常噁心的一道題。。。

正解的思路就非常玄學,非常非常玄學。。。

考慮判定權值在區間 \([l,r]\) 內的所有數所在的格子是否形成了一個矩形,因為題目保證各個點的值不相同,因此滿足條件的矩形一定包括了 \([Min,Max]\) 中的所有的點。

記值在 \([l,r]\) 格子的顏色為黑色,其它的格子顏色為白色。

考慮所有的 \((n + 1)\times(m + 1)\)\(2\times 2\) 的小正方形 (部分超出邊界也算)。

則所有黑色格子形成一個矩形,當且僅當恰好有 \(4\) 個小正方形內部有 \(1\) 個黑色格子,並且沒有任何一個小正方形內部有 \(3\) 個黑色格子,所謂有 \(1\) 個黑色格子的小正方形其實就是矩形的四個角,然後 有 \(2\) 個,\(4\) 個黑色格子的就是邊緣以及矩形內部的。

從小到大列舉 \(r\) ,對每個 \(l\le r\) ,記 \(f(l)\) 表示染黑權值在 \([l,r]\) 內的格子後,有多少小正方形內部有 1 個或 3 個黑色格子。

\(f(l) \ge 4,f(r) = 4\),於是只需要對每個 \(l\) 維護 \(f(l)\) 最小值,最小值的數目和取得最小值的所有 \(l\) 之和。

每次小格子的更改只會影響到周圍四個小正方形內部的黑色格子的數量,然後就是根據當前的格子狀態進行修改。

假設四個格子的大小從小到大是 \(l_1,l_2,l_3,r\) 那麼我們進行 對於 \(r\) 格子的染色之後就會有, \([l_3+1,r]\) 區間染色是有一個有 \(1\) 個黑色格子的小正方形,染色之前有 一個有 \(0\) 個黑色格子的小正方形。

因此我們給 \([l_3+1,r]\) 區間的 \(f(l)\)\(1\)。同樣的道理 \([l_2+1,l_3]\) 區間減去 \(1\)\([l_1+1,l_2]\) 區間加 \(1\) , \([1,l_1]\) 區間減去 \(1\)

線段樹維護即可,注意一個點,一開始的 \(f(l)\)\(0\) 我們是不會算入最小值的,但是當我們區間加值之後就會被計入答案,因此需要維護每個區間剩餘 \(0\) 的數量以及 \(l\) 之和。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int mod=998244353,N=5e5+10,INF=1e18;
int n,m,ans,d[10],fro[N];
vector<int> s[N];
pair<int,int> id[N];
struct Segment_Tree{int cnt,dat,sum,laz,res,tot;}tre[N<<2];
void push_up(int x)
{
	tre[x].res=tre[ls].res+tre[rs].res;tre[x].tot=tre[ls].tot+tre[rs].tot;
	if(tre[ls].dat==tre[rs].dat) return tre[x].dat=tre[ls].dat,tre[x].cnt=tre[ls].cnt+tre[rs].cnt,tre[x].sum=tre[ls].sum+tre[rs].sum,void();
	if(!tre[ls].dat) return tre[x].dat=tre[rs].dat,tre[x].cnt=tre[rs].cnt,tre[x].sum=tre[rs].sum,void();
	if(!tre[rs].dat) return tre[x].dat=tre[ls].dat,tre[x].cnt=tre[ls].cnt,tre[x].sum=tre[ls].sum,void();
	if(tre[ls].dat>tre[rs].dat) return tre[x].dat=tre[rs].dat,tre[x].cnt=tre[rs].cnt,tre[x].sum=tre[rs].sum,void();
	if(tre[rs].dat>tre[ls].dat) return tre[x].dat=tre[ls].dat,tre[x].cnt=tre[ls].cnt,tre[x].sum=tre[ls].sum,void();
}
void build(int x,int l,int r)
{
	if(l==r) return tre[x].dat=0,tre[x].cnt=1,tre[x].sum=l,tre[x].res=1,tre[x].tot=l,fro[l]=x,void();
	int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); push_up(x);
}
void push_down(int x,int l,int r)
{
	if(!tre[x].laz) return ;
	int mid=(l+r)>>1;
	tre[ls].dat+=tre[x].laz;tre[ls].laz+=tre[x].laz;
	if(!tre[ls].dat) tre[ls].cnt=tre[ls].res=mid-l+1,tre[ls].sum=tre[ls].tot=(l-1)*(mid-l+1)+mid-l+1;
	if(tre[x].laz>0&&tre[ls].res) tre[ls].dat=tre[x].laz,tre[ls].cnt=tre[ls].res,tre[ls].sum=tre[ls].tot,tre[ls].res=tre[ls].tot=0;
	tre[rs].dat+=tre[x].laz;tre[rs].laz+=tre[x].laz;
	if(!tre[rs].dat) tre[rs].cnt=tre[rs].res=r-mid,tre[rs].sum=tre[rs].tot=(mid-1)*(r-mid)+r-mid;
	if(tre[x].laz>0&&tre[rs].res) tre[rs].dat=tre[x].laz,tre[rs].cnt=tre[rs].res,tre[rs].sum=tre[rs].tot,tre[rs].res=tre[rs].tot=0;
	tre[x].laz=0;
}
void insert(int x,int l,int r,int L,int R,int num)
{
	if(L<=l&&r<=R)
	{
		tre[x].laz+=num; tre[x].dat+=num;
		if(!tre[x].dat) tre[x].cnt=r-l+1,tre[x].sum=(l-1)*(r-l+1)+r-l+1,tre[x].res=r-l+1,tre[x].tot=(l-1)*(r-l+1)+r-l+1;
		if(num&&tre[x].res) tre[x].dat=tre[x].laz,tre[x].cnt=tre[x].res,tre[x].sum=tre[x].tot,tre[x].res=tre[x].tot=0;
		return ;
	}
	int mid=(l+r)>>1; push_down(x,l,r);
	if(L<=mid) insert(ls,l,mid,L,R,num);
	if(R>mid) insert(rs,mid+1,r,L,R,num);
	push_up(x);
}
void work(int num)
{
	sort(d+1,d+5);int pos=lower_bound(d+1,d+5,num)-d;d[0]=0;
	for(int j=1,temp=pos-j+1;j<=pos;j++,temp=pos-j+1)
		if(temp==1) insert(1,1,n*m,d[j-1]+1,d[j],1);
		else if(temp==2) insert(1,1,n*m,d[j-1]+1,d[j],-1);
		else if(temp==3) insert(1,1,n*m,d[j-1]+1,d[j],1);
		else if(temp==4) insert(1,1,n*m,d[j-1]+1,d[j],-1);
}
signed main()
{
	freopen("pig.in","r",stdin); freopen("pig.out","w",stdout);
	n=read(); m=read(); build(1,1,n*m);
	for(int i=0;i<=m+1;i++) s[0].push_back(n*m+1),s[n+1].push_back(n*m+1);
	for(int i=1;i<=n;i++) s[i].push_back(n*m+1);
	for(int i=1;i<=n;i++)for(int j=1,x;j<=m;j++) x=read(),s[i].push_back(x),id[x]=make_pair(i,j);
	for(int i=1;i<=n;i++) s[i].push_back(n*m+1);
	for(int i=1;i<=n*m;i++)
	{
		int x=id[i].first,y=id[i].second,pos;
		d[1]=s[x-1][y];d[2]=s[x][y-1];d[3]=s[x-1][y-1];d[4]=i;work(i);
		d[1]=s[x-1][y];d[2]=s[x][y+1];d[3]=s[x-1][y+1];d[4]=i;work(i);
		d[1]=s[x+1][y];d[2]=s[x][y-1];d[3]=s[x+1][y-1];d[4]=i;work(i);
		d[1]=s[x+1][y];d[2]=s[x][y+1];d[3]=s[x+1][y+1];d[4]=i;work(i);
		if(tre[1].dat==4) ans=(ans+i*tre[1].cnt%mod-tre[1].sum+tre[1].cnt+mod)%mod;
	}
	printf("%lld",ans);
	return 0;
}

T4 抑鬱刀法

欲學此法,必先抑鬱,由於我還沒有抑鬱,因此。。。

大坑未補