Title

六省联考2017题解

[六省联考 2017]组合数问题

观察数据范围,发现\(n\)非常大,但是\(k\)\(r\)很小,容易想到矩阵乘法。

原题式子的组合意义就是从\(n \times k\)个物品选择\(i(i \bmod k=r)\)个物品。

考虑dp,设\(f_{i,j}\)表示从\(i\)个物品中选择\(s(s \bmod k=j)\)个物品。

由于\(C_{i,j}=C_{i-1,j-1}+C_{i-1,j}\),因此可以推出\(f_{i,j}=f_{i-1,j}+f_{i-1,(j-1) \bmod k}\)

发现所有的\(f_{i,j}\)都是从\(i-1\)转移过来的,因此可以矩阵乘法优化。

复杂度:\(O(k^3 \log n)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=120;
int n,k,r,mod;
struct Matrix
{
	int n,m;
	int c[maxn][maxn];
	Matrix()
	{
		n=0,m=0;
		for(int i=0;i<maxn;i++)
			for(int j=0;j<maxn;j++)
				c[i][j]=0;
	}
	Matrix operator * (Matrix B)const
	{
		Matrix C;C.n=n,C.m=B.m;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=B.m;j++)
				for(int k=1;k<=m;k++)
					C.c[i][j]=(C.c[i][j]+1ll*c[i][k]*B.c[k][j]%mod)%mod;
		return C;
	}
	Matrix power(long long k)
	{
		Matrix C,ans;C.n=ans.n=n,C.m=ans.m=m;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				C.c[i][j]=c[i][j];
		for(int i=1;i<=n;i++)
			ans.c[i][i]=1;
		while(k)
		{
			if(k&1ll)
				ans=ans*C;
			C=C*C;
			k>>=1ll;
		}
		return ans;
	}
}Ans,F;
int main()
{
	scanf("%d%d%d%d",&n,&mod,&k,&r);
	F.n=k,F.m=1;
	F.c[1][1]=1;
	Ans.n=Ans.m=k;
	for(int i=1;i<=k;i++)
	{
		int now=i-1;
		if(now==0)
			now=k;
		Ans.c[i][i]+=1;Ans.c[i][now]+=1;
	}
	Ans=Ans.power(1ll*n*k);
	F=Ans*F;
	printf("%d\n",F.c[r+1][1]);
	
	return 0;
}

[六省联考2017]期末考试

由于同学的不满意值只和最后一科出的时间有关,因此我们设\(T\)表示最后一科出的时间。

枚举\(T\),用前缀和统计出同学的不满意值。

然后考虑时间调整造成的代价:

  • \(A < B\),先用时间不足\(T\)的学科去补时间大于\(T\)的学科,如果还有时间大于\(T\)的学科才使用2操作
  • \(A > B\),直接使用2操作。
  • \(A = B\),均可。

用前缀和、后缀和统计一下答案就可以了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=500000;
int A,B,C,n,m,t[maxn],b[maxn],sumt[maxn],sum1[maxn],sum2[maxn];
signed main()
{
	scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%lld",&t[i]);
	sort(t+1,t+n+1);
	for(int i=1;i<=n;i++)
		sumt[i]=sumt[i-1]+t[i];
	for(int i=1;i<=m;i++)
		scanf("%lld",&b[i]);
	sort(b+1,b+m+1);
	for(int i=1;i<=m;i++)
		sum1[i]=sum1[i-1]+b[i];
	for(int i=m;i>=1;i--)
		sum2[i]=sum2[i+1]+b[i];
	int ans=1e15;
	int t1=0,t2=0;
	for(int i=1;i<=100000;i++)
	{
		while(t1<n&&t[t1+1]<=i)
			t1++;
		while(t2<m&&b[t2+1]<=i)
			t2++;
		__int128 now=(t1*i-sumt[t1])*(__int128)C;
		if(A>B)
			now+=(sum2[t2+1]-i*(m-t2))*B;
		else
		{
			int p1=t2*i-sum1[t2],p2=sum2[t2+1]-i*(m-t2);
			now+=min(p1,p2)*(__int128)A+max(0ll,p2-p1)*(__int128)B;
		}
		if(now<=1e15)
			ans=min((__int128)ans,now);
	}
	printf("%lld\n",ans);
	
	return 0;
}

[六省联考2017]分手是祝愿

暂咕

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=500000;
int A,B,C,n,m,t[maxn],b[maxn],sumt[maxn],sum1[maxn],sum2[maxn];
signed main()
{
	scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%lld",&t[i]);
	sort(t+1,t+n+1);
	for(int i=1;i<=n;i++)
		sumt[i]=sumt[i-1]+t[i];
	for(int i=1;i<=m;i++)
		scanf("%lld",&b[i]);
	sort(b+1,b+m+1);
	for(int i=1;i<=m;i++)
		sum1[i]=sum1[i-1]+b[i];
	for(int i=m;i>=1;i--)
		sum2[i]=sum2[i+1]+b[i];
	int ans=1e15;
	int t1=0,t2=0;
	for(int i=1;i<=100000;i++)
	{
		while(t1<n&&t[t1+1]<=i)
			t1++;
		while(t2<m&&b[t2+1]<=i)
			t2++;
		__int128 now=(t1*i-sumt[t1])*(__int128)C;
		if(A>B)
			now+=(sum2[t2+1]-i*(m-t2))*B;
		else
		{
			int p1=t2*i-sum1[t2],p2=sum2[t2+1]-i*(m-t2);
			now+=min(p1,p2)*(__int128)A+max(0ll,p2-p1)*(__int128)B;
		}
		if(now<=1e15)
			ans=min((__int128)ans,now);
	}
	printf("%lld\n",ans);
	
	return 0;
}
posted @ 2022-03-08 20:13  五百年前  阅读(41)  评论(0编辑  收藏  举报