2024暑假集訓測試28

卡布叻_周深發表於2024-08-19

前言

  • 比賽連結

上午要輸液所以沒有打,就下午改一改,應該明天就能回去了。

T1 與和

  • 原題:[ABC238D] AND and SUM

\(x\&y=a\),說明 \(x,y\) 二進位制中都包含 \(a\) 且其餘位上均不重合,故此若 \((s-2a)\&a=0\) 即符合,特殊的,因為 \(x\&y=a\le \min(x,y)\),所以 \(x+y=s\ge 2a\),需要特判。

T2 函式

  • 原題:[ABC366F] Maximum Composition

對於 \(f_1(f_2(x))\ge f_2(f_1(x))\),有 \(a_1a_2x+a_1b_2+b_1\ge a_2a_1x+a_2b_1+b_2\),移項有 \(\dfrac{b_1}{a_1-1}\le \dfrac{b_2}{a_2-1}\),故此將 \(\dfrac{b_i}{a_i-1}\) 作為關鍵字降序排序即可作為決策順序。

之後直接 DP 轉移即可,設 \(f_{i,j}\) 位前 \(i\) 個數中選擇了 \(j\) 個時的最大值,有:

\[f_{i,j}=\max(f_{i-1,j},f_{i-1,j-1}\times a_i+b_i) \]

第一維可以滾掉。

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
	for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,k;
ll f[11];
struct aa {int a,b;}e[N];
bool cmp(aa a,aa b) {return a.b*(b.a-1)>b.b*(a.a-1);}
signed main()
{
	read(n,k);
	for(int i=1;i<=n;i++) read(e[i].a,e[i].b);
	sort(e+1,e+1+n,cmp);
	f[0]=1;
	for(int i=1;i<=n;i++)
		for(int j=min(i,k);j>=1;j--) 
			f[j]=max(f[j],f[j-1]*e[i].a+e[i].b);
	write(f[k]);
}

T3 袋鼠

  • 原題:P5999 [CEOI2016] kangaroo

和某次的 DP 搬運工是同一種題型,預設型 DP。

原問題可以轉化為 \(1\sim n\) 的全排中能夠滿足對於任意 \(a_i\) 滿足 \(a_{i-1}<a_1,a_i>a_{i+1}\)\(a_{i-1}>a_i,a_i<a_{i+1}\) 的有多少。

那麼考慮預設型 DP,從小到大插數,設 \(f_{i,j}\) 為前 \(i\) 個數分成了 \(j\) 段,那麼有:

  • \(i\ne s\)\(i\ne t\)

    • \(i\) 新開了一段,因為後面插入的數能接到他兩邊的一定都比他大,所以一定合法,加入前共有 \(j-1\) 段所以有 \(j\) 個位置可以放但是若 \(i>s\) 就不能放開頭了,同理 \(i>t\) 就不能放結尾了,故有:

      \[f_{i,j}+=(j-[i>s]-[i>t])\times f_{i-1,j-1} \]

    • \(i\) 接在一段的開頭或結尾且並不使兩段接壤,因為此時與他相鄰的數一定小於他,而後面再加入的與其相鄰的數一定大於他,故一定不合法。

    • \(i\) 使兩段接壤,因為此時與其相鄰的兩個數一定都小於他,所以一定合法,在進行這一步前有 \(j+1\) 個段產生 \(j\) 個間隙,故此有:

      \[f_{i,j}+=j\times f_{i-1,j+1} \]

  • \(i=s\)\(i=t\)

    此時他是一定接在開頭或結尾的,故最終只有一個數與其相鄰,因為之前加入的數都比他小,所以一定合法,同時不存在使兩段接壤的情況,只需要考慮新開一段和接在原來段的開頭或結尾即可,其所填位置唯一,故有:

    \[f_{i,j}=f_{i-1,j-1}+f_{i-1,j} \]

最終一定只有一段,故答案為 \(f_{n,1}\),第一維可以滾掉,那麼類似的之前做的 P2467 [SDOI2010] 地精部落 這題也可以用預設型 DP 直接做了,DP 多開兩維表示當前首尾選沒選就行了。

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e3+10,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
	for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,s,t,f[2][N];
signed main()
{
	read(n,s,t);
	f[1][1]=1;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++)
		{
			if(i!=s&&i!=t) f[i&1][j]=(1ll*f[(i-1)&1][j-1]*(j-(i>s)-(i>t))%P+1ll*f[(i-1)&1][j+1]*j%P)%P;
			else f[i&1][j]=f[(i-1)&1][j-1]+f[(i-1)&1][j];
		}
	write(f[n&1][1]);
}

T4 最短路

  • 原題:CF464E The Classic Problem

還沒有打。