3.31省选模拟

很久以后,还会是我一个人,站在风里,听着自己的心事

\(5.20k\)提交祭

\(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\)

毒瘤数据结构,待补

posted @ 2022-03-31 16:57  Authentic_k  阅读(38)  评论(0编辑  收藏  举报