1. 程式人生 > 其它 >3.31省選模擬

3.31省選模擬

\(T1\)

轉化題意,恰好取出\(k\)條鏈,使得權值儘可能大

首先貪心的想,每次都選出來最大的即可,然後這樣顯然不能保證最優吧

首先選一個,肯定是要選最大的,那麼我們考慮這次操作可能對下次產生影響

我們第二次選,可能需要撤銷一部分第一步的操作,然後選出一條最長鏈

考慮正確性,我們要假設目前存在更優的選擇選另一條鏈,那還是表示加上一個最大的權值,那麼我們這麼選的話,肯定能選出來

本質是就是反悔貪心吧,模擬費用流的過程

發現,我對於貪心的證明一直都不是很容易理解,或許思維過於僵化了

當然這道題可以\(wqs\)二分,至於凸性的話,還有種證明方法,如果能把模型轉化為費用流形式就滿足凸性

#include<bits/stdc++.h>
#define INF 2147483647
#define MAXN 200005
using namespace std;
vector<int>lu[MAXN];
vector<pair<int,int> >rd[MAXN];
int dp1[MAXN],dp2[MAXN],val[MAXN],nxt1[MAXN],nxt2[MAXN];
int n,k,Ans;
void dfs_pre(int now,int fa)
{
	 for(int i=0;i<rd[now].size();i++)
	 {
	 	 int y=rd[now][i].first;
	 	 if(y==fa) continue;
	 	 val[y]=rd[now][i].second;
	 	 lu[now].push_back(y);
	 	 dfs_pre(y,now);
	 }
}
void dfs(int now)
{
     for(int i=0;i<lu[now].size();i++)
     {
     	 int y=lu[now][i];
     	 dfs(y);
     	 if(dp1[y]+val[y]>dp1[now])
     	 {
     	    nxt2[now]=nxt1[now];
			dp2[now]=dp1[now];
			nxt1[now]=y;
			dp1[now]=dp1[y]+val[y];
		 }
		 else if(dp1[y]+val[y]>dp2[now])
		 {
		 	nxt2[now]=y;
			dp2[now]=dp1[y]+val[y];
		 }
	 }
//	 cout<<"now: "<<now<<" "<<dp2[now]<<" "<<nxt2[now]<<endl;
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1,u,v,w;i<n;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
        rd[u].push_back(make_pair(v,w));
		rd[v].push_back(make_pair(u,w));
	}
	dfs_pre(1,1);
	for(int i=1;i<=k;i++)
	{
		memset(dp1,0,sizeof(dp1));
		memset(dp2,0,sizeof(dp2));
		memset(nxt1,0,sizeof(nxt1));
		memset(nxt2,0,sizeof(nxt2));
		dfs(1);
		int Max=-INF,rt;
        for(int j=1;j<=n;j++)
        {
        	if(Max<dp1[j]+dp2[j])
		    {
		       Max=dp1[j]+dp2[j];
		       rt=j;
			}
		}
//		cout<<"Max: "<<Max<<"\n";
		Ans+=Max;
		int now=rt;
		while(nxt1[now])
		{
			  now=nxt1[now];
			  val[now]*=-1;
		}
		now=nxt2[rt];
//		cout<<"nxt2: "<<now<<endl;
		while(now)
		{
			  val[now]*=-1;now=nxt1[now];
//			  cout<<"val: "<<val[now]<<"\n";
		}
	}
	cout<<Ans<<"\n";
}

\(T2\)

推式子的\(ppt\)\(87\)頁,很好,那咱們開推

(笑)大概要不少時間呢

\(sub_1:n<=10\)

考慮列舉所有二叉樹形態,沒想到題解的辦法,吃飯的時候糊了一個,由於二叉樹可以二進位制表示,那麼列舉所有的二進位制狀態就好了,複雜度\(O(2^nn),\)可以打出前十項的表

\(Sub_2\)

推式子

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
#define MAXN 2000005
using namespace std;
const int up=259;
inline int re() {
	int x = 0, p = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();}
	while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * p;
}
int p,inv[MAXN],pw[MAXN],dw[MAXN],*C=inv,n,T;
int iv3,iv6,iv15,iv120,c1,c2;
void Init() 
{
	inv[1]=1; 
	for(int i=2;i<MAXN;i++) inv[i]=(-inv[p%i])*(p/i)%p;
	iv3=inv[3]; 
	iv6=inv[6]; 
	iv15=inv[15]; 
	iv120=inv[120];
	c1=2*iv3%p;c2=5*inv[12]%p;
	for(int i=2;i<MAXN;i++)
	{
		inv[i]=inv[i-1]*inv[i]%p; 
	} 
	pw[0]=1;
	for(int i=1;i<MAXN;i++) 
	{
	    pw[i]=pw[i-1]*4%p;
		dw[i]=(i-1)*i%p;	
	}
	for(int i=1,j=1,fac=1;i<MAXN;i++,j+=2) 
	{
	    C[i]=inv[i]*inv[i]%p*(fac=fac*j%p*(j+1)%p)%p;	
	}
}
void Input()
{
	p=re(),T=re(); 
}
void Eternal_Battle()
{
	 while(T--) 
	 {
		int k=re(),n=re(); 
		if(k==1) 
		{
			if(n<3) 
			{ 
			   printf("0\n"); 
			   continue; 
			}
			int now=((2*n-4)*pw[n-3]%p+iv6*dw[n-1]%p*C[n-1]-iv3*dw[n-2]%p*C[n-2])%p;
			now=(now+p)%p; 
			printf("%lld\n",now);
		}
		else 
		{
			if(n<3) 
			{
			   printf("0\n"); 
			   continue;
			}
			int now=((2*n*pw[n-3]+iv120*dw[n]%p*C[n])%p*(n-2)+(iv15*(n-3)+c2)%p*dw[n-1]%p*C[n-1]+((iv15*(n-4)+c1)%p*dw[n-2]+n-2)%p*2*C[n-2]%p)%p;
			now=(now+p)%p; 
			printf("%lld\n",now); 
		}
	}
}
signed main() 
{
	freopen("contact.in","r",stdin);
	freopen("contact.out","w",stdout);
	Input();Init();
	Eternal_Battle();
	return 0;
}


\(T3\)

毒瘤資料結構,待補