1. 程式人生 > 其它 >noip多校21

noip多校21

好幾天沒寫部落格了,一直在改題,挺累的。今天的題實在有些不可改,最後剩下一些時間來寫一寫部落格,當做總結,也當做放鬆一下。

這次考試,掛分直接掛沒了,問題出現在兩個方面:
1.沒有計算時間複雜度,程式碼實現常數過大
2.沒有仔細讀題,沒有清楚題目讓幹什麼。
其實,這兩個問題都是可以避免的,對於計算時空複雜度,應該是一個習慣,不能憑感覺。
對於審題這件事,還是要細心,尤其是對於題目描述比較簡單的情況下,也可以多讀幾遍。如果始終過不了樣例也可以再讀一遍題。
剩下的就是考試策略問題,我覺得我的策略需要改進,之前都是看到那種純數學題,期望題就直接跳過,現在覺得這樣的做法是不可行的,因為最近幾天的考試題都只是披著一個期望的皮,實際上和概率那些東西沒什麼關係,只要想一想應該就可以想出來的。
然後就是對於考試心態,如果整套題題目都是偏難,首先要穩住心態,儘可能拿到自己可以拿到的分,以不掛分為主。

T1 按位或

咕了

T2 最短路徑

思路:如果我們規定從起點出發後還要回到起點,那麼我麼走過的路徑長度就是我經過的所有邊的距離2-起點到一個點的距離。
那麼假設這\(k\)個點已經確定,路徑長度就是經過的所有邊的距離
2-最長的鏈的距離。
考慮分別計算。
對於前一部分,我們可以在\(dfs\)的過程中求出。一條邊有貢獻當且僅當它兩端都右可以當做黑點的點。那麼這條邊被經過的方案數就是\(C_{m,k}-C_{sum,k}-C{m-sum,k}\),其中,\(sum\)表示這條邊下面的端點為根的子樹中的可以作為黑點的數量。
對於後一部分,我們列舉兩個點,將這兩個點之間的距離作為一條鏈,然後再列舉這條鏈可以對多少點產生貢獻,用\(ans-C(k-2,cnt)\times len\)

即可。
程式碼如下:

AC_code

#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define next netete
#define head haeaead
using namespace std;
const int N=2010;
const int mo=998244353;
int n,m,k,tot,ans;
bool vis[N];
int cun[N],jc[N],inv[N];
int to[N<<1],next[N<<1],head[N];
int deep[N],size[N],fa[N],top[N],son[N];
int dis[N][N],sum[N];
ii read()
{
	int x=0; char ch=getchar(); bool f=1;
	while(ch<'0' or ch>'9')
	{
		if(ch=='-') f=0;
		ch=getchar();
	}
	while(ch>='0' and ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}	
	return f?x:(-x);
}
ii ksm(int d,int z)
{
	int out=1;
	while(z)
	{
		if(z&1) out=out*d%mo;
		z>>=1;
		d=d*d%mo;
	}
	return out;
}
ii C(int n,int m)
{
	if(m>n) return 0;
	if(m==0 or n==0) return 1;
	return jc[n]*inv[n-m]%mo*inv[m]%mo;
}
iv add(int x,int y)
{
	to[++tot]=y;
	next[tot]=head[x];
	head[x]=tot;
}
iv dfs(int st,int f)
{
	fa[st]=f;
	size[st]=1;
	deep[st]=deep[f]+1;
	if(vis[st]) sum[st]=1;
	for(re i=head[st];i;i=next[i])
	{
		int p=to[i];
		if(p==f) continue;
		dfs(p,st);
		if(sum[p] and m-sum[p]) ans=(ans+C(m,k)-C(sum[p],k)-C(m-sum[p],k)+mo+mo)%mo;
		size[st]+=size[p];
		sum[st]+=sum[p];
		son[st]=(size[son[st]]>size[p])?son[st]:p;
	}
}
iv dfs2(int st,int t)
{
	top[st]=t;
	if(!son[st]) return ;
	dfs2(son[st],t);
	for(re i=head[st];i;i=next[i])
	{
		int p=to[i];
		if(p==son[st] or p==fa[st]) continue;
		dfs2(p,p);
	}
}
ii get_lca(int x,int y)
{
	int fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(deep[fx]<deep[fy]) swap(x,y),swap(fx,fy);
		x=fa[fx],fx=top[x];
	}
	return deep[x]<deep[y]?x:y;
}
signed main()
{
	freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
	n=read(),m=read(),k=read();
	int x,y;
	jc[0]=1;
	for(re i=1;i<=305;i++) jc[i]=jc[i-1]*i%mo;
	inv[305]=ksm(jc[305],mo-2);
	for(re i=304;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mo;
	for(re i=1;i<=m;i++) cun[i]=read(),vis[cun[i]]=1;
	for(re i=1;i<n;i++)
	{
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	dfs(1,0);dfs2(1,1);
	for(re i=1;i<=m;i++)
	{
		for(re j=i+1;j<=m;j++)
		{
			int lca=get_lca(cun[i],cun[j]);
			dis[cun[i]][cun[j]]=dis[cun[j]][cun[i]]=deep[cun[i]]+deep[cun[j]]-2*deep[lca];
		}	
	}
	ans=ans*2ll%mo;
	for(re i=1;i<=m;i++)
	{
		for(re j=i+1;j<=m;j++)
		{
			int cnt=0;
			for(re p=1;p<=m;p++)
			{
				if(p==i or p==j) continue;
				if(dis[cun[p]][cun[i]]<dis[cun[i]][cun[j]] and dis[cun[p]][cun[j]]<dis[cun[i]][cun[j]]) ++cnt;
				else if((dis[cun[p]][cun[i]]==dis[cun[i]][cun[j]]  and p<j) and (dis[cun[p]][cun[j]]==dis[cun[i]][cun[j]] and p<i)) ++cnt;
				else if(dis[cun[p]][cun[i]]==dis[cun[i]][cun[j]]  and p<j and dis[cun[p]][cun[j]]<dis[cun[i]][cun[j]]) ++cnt;
				else if(dis[cun[p]][cun[j]]==dis[cun[i]][cun[j]]  and p<i and dis[cun[p]][cun[i]]<dis[cun[i]][cun[j]]) ++cnt;
			}
			ans=(ans-C(cnt,k-2)*dis[cun[i]][cun[j]]%mo+mo)%mo;
		}
	}
	printf("%lld\n",ans*ksm(C(m,k),mo-2)%mo);
	return 0;	
}

T3 仙人掌

咕了

T4 對弈

咕了