NOIP模擬66

Varuxn發表於2021-10-08

T1 接力比賽

解題思路

其實就是一個揹包 DP ,也沒啥好說的也就是一個優化,每次列舉之前的字首和。

比較妙的就是一個 random_shuffle 可以整掉部分卡人的資料(但是好像 sort 一下效果更好。)

然而就算上面的方法不用也可以愉快的水過。。

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=1e3+10,M=N*N;
int n,m,ans,f1[M],f2[M],pre1[N],pre2[N];
struct Node{int w,v;}a[N],b[N];
signed main()
{
	freopen("game.in","r",stdin); freopen("game.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) a[i].w=read(),a[i].v=read(),pre1[i]=pre1[i-1]+a[i].w;
	for(int i=1;i<=m;i++) b[i].w=read(),b[i].v=read(),pre2[i]=pre2[i-1]+b[i].w;
	memset(f1,-1,sizeof(f1)); memset(f2,-1,sizeof(f2)); f1[0]=f2[0]=0;
	for(int i=1;i<=n;i++)
		for(int j=pre1[i-1];j>=0;j--)
			if(~f1[j]) f1[j+a[i].w]=max(f1[j+a[i].w],f1[j]+a[i].v);
	for(int i=1;i<=m;i++)
		for(int j=pre2[i-1];j>=0;j--)
			if(~f2[j]) f2[j+b[i].w]=max(f2[j+b[i].w],f2[j]+b[i].v);
	for(int i=1;i<=max(pre1[n],pre2[m]);i++)
		if((~f1[i])&&(~f2[i]))
			ans=max(ans,f1[i]+f2[i]);
	printf("%lld",ans);
	return 0;
}

T2 樹上競技

解題思路

對於一條邊而言兩側有 \(x,y\) 個關鍵點且一定滿足 \(x+y=m\) 假設 \(x<y\) 那麼一定會讓那 \(x\) 個點經過這條邊。

假設一條邊的兩邊分別有 \(s,n-s\) 個點 答案就是 \(\sum\limits_{i=1}^{m-1}\binom{i}{s}\binom{m-i}{n-s}\min(i,m-i)\)

\[\displaystyle ans=\sum_{i=1}^{\min(\frac{m-1}{2},s)}\binom{i}{s}\binom{m-i}{n-s}+\sum_{i=1}^{\min(\frac{m-1}{2},n-s)}\binom{i}{s}\binom{m-i}{n-s}+[m\bmod 2=0]\times\binom{\frac{m}{2}}{s}\binom{\frac{m}{2}}{n-s}\frac{m}{2} \]

對於柿子的最後一項直接暴算就可以,設 \(g(s)=\sum\limits_{i=1}^{\min(\frac{m-1}{2},s)}\binom{i}{s}\binom{m-i}{n-s}\)

於是我們只需要遞推出 \(g(s)\) 就好了 \(g(s)=\sum\limits_{i=1}^{\min(\frac{m-1}{2},s)}\binom{s-1}{i-1}\binom{m-i}{n-s}\) 發現對於 \(g(s)\)\(g(s+1)\) 而言兩個柿子是有公共部分的。

\(k=\frac{m-1}{2}\) 就有 \(g(s)=g(s-1)-\binom{k-1}{s-2}\binom{m-k-1}{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 mod=1e9+7,N=1e6+10,INF=1e18;
int n,m,ans,k,g[N],siz[N],fac[N],ifac[N],fa[N];
int tot,head[N],ver[N<<1],nxt[N<<1];
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
int power(int x,int y,int p=mod)
{
	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){if(x<y) return 0;return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
void dfs(int x)
{
	siz[x]=1;
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i],temp=0; dfs(to);
		siz[x]+=siz[to];
		ans=(ans+g[siz[to]]*siz[to]%mod+g[n-siz[to]]*(n-siz[to])%mod)%mod;
		if(!(m&1)) ans=(ans+C(siz[to],m/2)*C(n-siz[to],m/2)%mod*(m/2))%mod;
	}
}
signed main()
{
	freopen("meeting.in","r",stdin); freopen("meeting.out","w",stdout);
	n=read(); m=read(); k=(m-1)/2;
	for(int i=2;i<=n;i++) fa[i]=read(),add_edge(fa[i],i);
	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;
	if(k>0) g[1]=C(n-1,m-1); for(int i=2;i<=n;i++) g[i]=(g[i-1]-C(i-2,k-1)*C(n-i,m-k-1)%mod+mod)%mod;
	dfs(1); printf("%lld",ans);
	return 0;
}

T3 虛構推理

解題思路

關於正解二分。。。不會。。

但是仔細觀察題目好像是相對誤差,因此我們可以嘗試卡精度,列舉每一個時間,然後暴算。

具體思路就是現對於給出的時間求出來相應的時針分針的角度,然後分別進行排序。

對於每一個列舉到的時間同樣也要算出時針分針的角度(注意這裡以及以下的角度都是相對於十二點而言的)

然後對於距離某一種指標最遠的指標,我們取他加或者減去 \(180^。\) 的角度,然後在之前排好序的陣列上面二分一個前驅後繼。

這前驅後繼就是距離當前指標最遠的點,為了防止某些玄學情況,我們設 a[0]=a[n],a[n+1]=a[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=5e4+10,INF=1e9;
int n;
double a[N],b[N],ans=INF;
struct Node{int h,m,sec;}s[N];
void solve(double x,double y,double z)
{
	double h=30.0*x+0.5*y+0.5*z/60.0,m=6.0*y+0.1*z,h2,m2;
	h2=h; if(h2<180.0) h2+=180.0; else h2-=180.0;
	m2=m; if(m2<180.0) m2+=180.0; else m2-=180.0;
	int pos1=upper_bound(a+1,a+n+1,h2)-a-1;
	int pos2=upper_bound(b+1,b+n+1,m2)-b-1;
	double tmp1=180.0-h2+a[pos1],tmp3=180.0-m2+b[pos2]; 
	double tmp2=180.0-a[pos1+1]+h2,tmp4=180.0-b[pos2+1]+m2;
	if(tmp1<0) tmp1+=360.0; if(tmp1>=360) tmp1-=360.0;
	if(tmp2<0) tmp2+=360.0; if(tmp2>=360) tmp2-=360.0;
	if(tmp3<0) tmp3+=360.0; if(tmp3>=360) tmp3-=360.0;
	if(tmp4<0) tmp4+=360.0; if(tmp4>=360) tmp4-=360.0;
	tmp1=min(tmp1,360.0-tmp1); tmp2=min(tmp2,360.0-tmp2);
	tmp3=min(tmp3,360.0-tmp3); tmp4=min(tmp4,360.0-tmp4);
	ans=min(ans,max(max(tmp1,tmp2),max(tmp3,tmp4)));
}
signed main()
{
	freopen("unreal.in","r",stdin); freopen("unreal.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) s[i].h=read()%12,s[i].m=read(),s[i].sec=read();
	for(int i=1;i<=n;i++) a[i]=s[i].h*30.0+s[i].m*0.5+s[i].sec*0.5/60.0,b[i]=s[i].m*6.0+s[i].sec*0.1;
	sort(a+1,a+n+1); a[n+1]=a[1]; a[0]=a[n];
	sort(b+1,b+n+1); b[n+1]=b[1]; b[0]=b[n];
	for(int i=0;i<12;i++)
		for(int j=0;j<60;j++)
			for(double k=0;k<60;k+=0.01)
				solve(i,j,k);
	printf("%.6lf",ans);
	return 0;
}

T4 記憶碎片

解題思路

先算出來 \(n\) 的所有整數劃分,然後暴掃每一種權值。

對於某一權值情況只有兩種,一種是已經聯通的塊之間的連邊,直接計算方案數即可。

另一種就是將兩個聯通塊連結起來的邊,連完之後的集合也可以在整數劃分中找到。

列舉整數劃分以及查詢的時候可以開一個 map 對於每一種劃分對映一個編號,對於編號轉移。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"
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=50,M=4e4+10,mod=1e9+7;
int n,m,all,f[N*N][M],e[M];
bool vis[N*N];
vector<int> v,s[M];
map<vector<int>,int> mp;
void dfs(int las,int res)
{
	if(!res)
	{
		s[++all]=v; mp.insert(make_pair(v,all));
		for(int i=0;i<v.size();i++)
			e[all]=(e[all]+v[i]*(v[i]-1)/2)%mod;
		return ;
	}
	for(int i=las;i<=res;i++)
	{
		v.push_back(i);
		dfs(i,res-i);
		v.pop_back();
	}
}
signed main()
{
	freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
	n=read(); m=n*(n-1)/2;
	for(int i=1,x;i<n;i++) x=read(),vis[x]=true;
	dfs(1,n); f[0][1]=1;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=all;j++)
			if(f[i-1][j])
			{
				if(!vis[i]){f[i][j]=(f[i][j]+f[i-1][j]*(e[j]-i+1))%mod;continue;}
				for(int p=0;p<s[j].size();p++)
					for(int q=p+1;q<s[j].size();q++)
					{
						vector<int> temp;
						int x=s[j][p],y=s[j][q];
						temp.push_back(x+y);
						for(int k=0;k<s[j].size();k++)
							if(k!=p&&k!=q)
								temp.push_back(s[j][k]);
						sort(temp.begin(),temp.end());
						int pos=mp.find(temp)->second;
						f[i][pos]=(f[i][pos]+x*y%mod*f[i-1][j])%mod;
					}
			}
	printf("%lld",f[m][all]);
	return 0;
}