P2831 [NOIP2016 提高組] 憤怒的小鳥

rgw2010發表於2024-08-04

思路:

考慮先求出經過 \((x_1,y_1),(x_2,y_2)\) 的拋物線解析式

我們有:

\[\begin{cases} ax_1^2 + bx_1 = y_1 \\ ax_2^2 + bx_2 = y_2\end{cases} \]

考慮將 \(b\) 消掉,求出 \(a\)

那麼考慮令 \(1\) 式減去 \(2\) 式的 \(\frac{x_1}{x_2}\) 倍:

\[ax_1^2 + bx_1 - ax_1x_2 - bx_1 = y_1 - \frac{x_1}{x_2} y_2 \]

\[a(x_1^2 -x_1x_2) = y_1 - \frac{x_1}{x_2} y_2 \]

\[a = \frac{y_1 - \frac{x_1}{x_2} y_2}{x_1^2 -x_1x_2} = \frac{y_1x_2 - x_1 y_2}{x_1^2x_2 - x_1x_2^2} \]

因為 \(a\) 太複雜度,不好帶入,考慮也將 \(a\) 消掉,求出 \(b\)

那麼考慮令 \(1\) 式減去 \(2\) 式的 \(\frac{x_1^2}{x_2^2}\) 倍:

\[ax_1^2 + bx_1 - ax_1^2 - b\frac{x_1^2}{x_2} = y_1 - y_2 \frac{x_1^2}{x_2^2} \]

\[b(x_1 - \frac{x_1^2}{x_2}) = y_1 - y_2 \frac{x_1^2}{x_2^2} \]

\[b = \frac{y_1 - y_2 \frac{x_1^2}{x_2^2}}{x_1 - \frac{x_1^2}{x_2}} = \frac{y_1x_2^2 - y_2 x_1^2}{x_1x_2^2 - x_1^2 x_2} \]

那麼我們可以得到經過 \((x_1,y_1),(x_2,y_2)\) 的拋物線解析式為:

\[y = \frac{y_1x_2 - x_1 y_2}{x_1^2x_2 - x_1x_2^2} x^2 + \frac{y_1x_2^2 - y_2 x_1^2}{x_1x_2^2 - x_1^2 x_2} x \]

可以透過這個解析式求出其它在這個拋物線上的點,設 \(A_{i,j}\) 表示經過點 \(i\) 和點 \(j\) 的拋物線經過的所有點的點集。

考慮狀態壓縮 dp,令 \(dp_S\) 表示 \(S\) 這個點集的鳥被射下來的最小次數,則:

\[dp_0 = 0 \]

\[dp_{S \operatorname{or} A_{i,j}} = \min(dp_{S \operatorname{or} A_{i,j}},dp_S + 1) \]

\[dp_{S \operatorname{or} 2^{i-1}} = \min(dp_{S \operatorname{or} 2^{i-1}},dp_S+1) \]

時間複雜度為 \(O(Tn^22^n)\)

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=19,M=(1ll<<N)+10;
const db Eps=1e-6;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll T,n;
ll a[N][N];
ll dp[M];
db x[N],y[N];
pair<db,db> get(db x1,db y1,db x2,db y2){
	db a=(y1*x2-x1*y2)/(x1*x1*x2-x1*x2*x2);
	db b=(y1*x2*x2-y2*x1*x1)/(x1*x2*x2-x1*x1*x2);
	return {a,b};
}
db get(db x,db a,db b){
	return a*x*x+b*x;
}
void solve(){
	memset(dp,0x7f,sizeof(dp));
	n=read(),read();
	for(int i=1;i<=n;i++)
	  scanf("%lf%lf",&x[i],&y[i]);
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			a[i][j]=0;
			auto t=get(x[i],y[i],x[j],y[j]);
			if(t.fi>=0)
			  continue;
			for(int k=1;k<=n;k++){
				db h=get(x[k],t.fi,t.se);
				if(abs(h-y[k])<=Eps)
				  a[i][j]|=(1ll<<(k-1));
			}
		}
	} 
	dp[0]=0;
	for(int S=0;S<(1ll<<n);S++){
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				if((a[i][j]&S)==a[i][j])
				  continue;
				dp[S|a[i][j]]=min(dp[S|a[i][j]],dp[S]+1);
			}
		}
		for(int i=1;i<=n;i++)
		  dp[S|(1ll<<(i-1))]=min(dp[S|(1ll<<(i-1))],dp[S]+1);
	} 
	write(dp[(1ll<<n)-1]);
	putchar('\n');
}
bool End;
int main(){
	T=read();
	while(T--)
	  solve();
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}

相關文章