2019/8/29 校内模拟赛 考试报告

A.准备(preparing.in)

已知\(p\)为大于\(2\)的质数,从\(1...2p\)中选择\(p\)个数,使得这\(p\)个数的和能被\(p\)整除,求总方案数.

思路1:(\(p\le 30\))

动态规划打表.

#include<bits/stdc++.h>

long long Ans[]={0,0,0,8,0,52,0,492,0,0,0,64132,0,800048,0,0,0,137270956,0,1860277044,0,0,0,357975249028,0,0,0,0,0,1036802293087624,0};
int n,x;

int main()
{
	freopen("preparing.in","r",stdin);
	freopen("preparing.out","w",stdout);
	std::cin>>n;
	while(n--)
	{
		std::cin>>x;
		std::cout<<Ans[x]<<std::endl;
	}
	return 0;
}

思路2:(\(p\le 1000\))

我们先排除掉选择\(1...p\)和选择\(p+1...2p\)这两种情况,剩下\((\dbinom{p}{2p}-2)\)种情况.

那么问题转化为在\(\{1,2,...,p-1,p\}\)中选\(k\)个数\((1\le k < p)\),在\(\{p+1,p+2,...,2p-1,2p\}\)中选\((p-k)\)个数,两组中选的数之和能被\(p\)整除的方案数.

容易发现,\(\{1,2,...,p-1,p\}\)\(\{p+1,p+2,...,2p-1,2p\}\)这两组数在模\(p\)意义下是相同的,不妨先讨论其中一组.

假设我们在第一组中选了\(k\)个数,记为\(\{a_1,a_2,...,a_k\}\).

我们把所有可以表示为\(\{(a_1+d)\mod p,(a_2+d)\mod p,...(a_k+d)\mod p\}\)的选法归为一,其中\(d \in[0,p-1]\),那么每一类中,选择的数的和对\(p\)的余数为\(0\)的选法占了\(1/p\),对\(p\)的余数为\(1\)的选法占了\(1/p\),...,对\(p\)的余数为\((p-1)\)的选法占了\(1/p\).

由此可以推出,在第一组中选\(k\)个的所有选法中,和对\(p\)的余数为\(0\)的占\(1/p\),对\(p\)的余数为\(1\)的选法占\(1/p\),...,对\(p\)的余数为\((p-1)\)的占\(1/ p\).这是因为,虽然我们并不知道的个数,但每一类中的每个余数的选法的占比是不变的,都是\(1/p\).

现在我们考虑枚举第一组中选出的\(k\)个数的和对\(p\)的余数.

假设是\(0\),占第一组总选法的\(1/p\),那么第二组中选出的\(p-k\)个数的和对\(p\)的余数也必须是\(0\),占第二组总选法的\(1/p\),由于在第一组和第二组里选数是两个独立事件,所以上述情况中合法选法占两组总选法的\(\dfrac{1}{p^2}\).

假设是\(1\),占第一组总选法的\(1/p\),那么第二组中选出的\(p-k\)个数的和对\(p\)的余数必须是\(p- 1\),占第二组总选法的\(1/p\),由于在第一组和第二组里选数是两个独立事件,所以上述情况中合法选法占两组总选法的\(\dfrac{1}{p^2}\).

...

假设是\(p-1\),占第一组总选法的\(1/p\),那么第二组中选出的\(p-k\)个数的和对\(p\)的余数必须是\(1\),占第二组总选法的\(1/p\),由于在第一组和第二组里选数是两个独立事件,所以上述情况中合法选法占两组总选法的\(\dfrac{1}{p^2}\).

那么在第一组中选了\(k\)个数,在第二组中选\((p-k)\)个数的合法选法占总选法的比例为\(\dfrac{1}{p^2} \times p=1/p\).

容易发现,当\(k\)变化时,上述思路可以推广.

那么在\((\dbinom{p}{2p}-2)\)种情况中,合法选法占\(1/p\).

再加上最开始暂时排除的两种情况,总方案数为:

\[\dfrac{\dbinom{p}{2p}-2}{p}+2 \]

B.奖学金(scholarship.cpp)

[USACO 2004 Mar]Financial Aid 赞助学费

[TJOI2013]奖学金

首先将学生按成绩由小到大排序,考虑枚举中位数.

值得注意的是,本题答案不具有二分性质,不能二分,但我考试的时候二分竟然有70分,数据实在是太水了.

预处理\(L(i)\)表示学生\([1,i)\)中花费最小的\(\lfloor\dfrac{n}2\rfloor\)个学生的总花费,\(R(i)\)同理.

那么从左到右扫描,寻找满足\(L(i)+R(i)+cost(i)\le F\)的最靠右的学生,它的成绩即为答案.

#include<bits/stdc++.h>
const int SIZE=200005,INF=10005;
int n,C,F,L[SIZE],R[SIZE];

struct Stu
{
	int S,x;
	bool operator <(const Stu &u)const
	{
		return S<u.S;
	}
}stu[SIZE];
std::priority_queue<int>q;

int main()
{
	scanf("%d%d%d",&n,&C,&F);
	n/=2;
	for(int i=1;i<=C;i++)
		scanf("%d%d",&stu[i].S,&stu[i].x);
	std::sort(stu+1,stu+1+C);
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		sum+=stu[i].x;
		q.push(stu[i].x);
	}
	for(int i=n+1;i<=C;i++)
	{
		L[i]=sum;
		if(q.top()>stu[i].x)
		{
			sum-=q.top();
			q.pop();
			q.push(stu[i].x);
			sum+=stu[i].x;
		}
	}
	while(q.size())q.pop();
	sum=0;
	for(int i=C;i>=C-n+1;i--)
	{
		sum+=stu[i].x;
		q.push(stu[i].x);		
	}
	for(int i=C-n;i;i--)
	{
		R[i]=sum;
		if(q.top()>stu[i].x)
		{
			sum-=q.top();
			q.pop();
			q.push(stu[i].x);
			sum+=stu[i].x;			
		}
	}	
	int Ans=-1;
	for(int i=n+1;i<=C-n;i++)
	{
		if(L[i]+R[i]+stu[i].x<=F)Ans=stu[i].S;
	}
	printf("%d",Ans);
	return 0;
}

C.巧克力(chocolate.cpp)

CF633F

给定一颗点权无根树,求两条不相交的链的权值和的最大值.

神仙DP题.

在参考了很多份题解之后,终于懂了是如何DP的.

我们以任意节点为根,DFS,维护以下四个值:

\(Long(x)\)表示\(x\)到它的子树中的最远叶子的距离,换句话说,就是经过\(x\),并且可以向上继续拓展的最长链.

\(DP1(x)\)表示\(x\)子树中最长的一条链的长度.

\(DP2(x)\)表示\(x\)子树中最长的两条不相交链的长度和.

\(DP3(x)\)表示\(x\)子树中一条链和另一条\(x\)到某个叶子的链的最大长度和,换句话说,就是一条链和另一条可以继续向上扩展的链的最大长度和.

在转移时,讨论各个值所有可能的产生情况,取最优解进行转移.

关于怎么转移,我还有很多绝妙的想法,但现在时间不够了,我不能把它们写下来

#include<bits/stdc++.h>
#define LL long long
#define IL inline

const int SIZE=200005;
int head[SIZE],nex[SIZE],ver[SIZE],Tot;
LL weight[SIZE];

LL DP1[SIZE];//子树中选一条链的最大长度
LL DP2[SIZE];//子树中选两条链的最大长度和 
LL DP3[SIZE];//子树中选一条链和另一条到叶子节点的链的最大长度和
LL Long[SIZE];//到叶子节点的链的最大长度

void Link(int u,int v)
{
	nex[++Tot]=head[u];head[u]=Tot;ver[Tot]=v;
	nex[++Tot]=head[v];head[v]=Tot;ver[Tot]=u;
}

IL LL m2(LL A,LL B){return A>B?A:B;}
IL LL m3(LL A,LL B,LL C){return m2(A,B)>C?m2(A,B):C;}
IL LL m4(LL A,LL B,LL C,LL D){return m3(A,B,C)>D?m3(A,B,C):D;}
IL LL m5(LL A,LL B,LL C,LL D,LL E){return m4(A,B,C,D)>E?m4(A,B,C,D):E;}

void DFS(int u,int F)
{
	Long[u]=weight[u];
	DP1[u]=weight[u];
	LL o=0;
	for(int i=head[u];i;i=nex[i])
	{
		int v=ver[i];
		if(v==F)continue;
		DFS(v,u);
		LL Tem1=DP1[u],Tem3=DP3[u];
		DP1[u]=m3(DP1[u],DP1[v],Long[u]+Long[v]);
		DP2[u]=m5(DP2[u],DP2[v],Long[u]+DP3[v],Long[v]+Tem3,Tem1+DP1[v]);
		DP3[u]=m4(DP3[u],DP3[v]+weight[u],o+(weight[u]+Long[v]),Long[u]+DP1[v]);
		Long[u]=m2(Long[u],Long[v]+weight[u]);
		o=m2(o,DP1[v]);
	} 
}

int main()
{
	std::ios::sync_with_stdio(false); 
	int n,u,v;
	std::cin>>n;
	for(int i=1;i<=n;i++)std::cin>>weight[i];
	for(int i=1;i<n;i++){std::cin>>u>>v;Link(u,v);}
	DFS(1,0);
	std::cout<<DP2[1];
	return 0;
}
posted @ 2019-08-29 19:51  TaylorSwift13  阅读(186)  评论(0编辑  收藏  举报