六省联考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;
}