六省联考2017总结

Day1 T1 期末考试

Sol

一开始想的是二分(wtcl),\(check\)的时候发现要求的东西可以均摊\(O(1)\)...,再加上发现了问题没有单调性。所以,可以暴力枚举的题目为什么要二分啊qwq

直接枚举最大的\(b\)值(即出成绩的日子),设它为\(i\),于是花费的代价=学生等待的时间+将大于\(i\)\(b\)值降低到\(i\)的花费。显然,\(i\)越小,学生等待的时间越小,这个代价具有单调性,可以均摊\(O(1)\)求;对于后者,我们用\(lar\)表示\(\Sigma{b_j-i},(b_j> i)\),即多出来的时间,用lit表示\(\Sigma{i-b_j},(b_j\leq i)\),即少的时间,当\(B<A\)时,当然直接全选择\(B\)操作最好,否则就先将\(lit\)这个空出来的坑用\(A\)操作填满,然后剩下的再用\(B\)操作,然后又可以发现\(lar\)\(lit\)也有单调性,也可以均摊\(O(1)\)求,于是整道题就变成了一道\(O(n)\)的模拟题

Code

#include<bits/stdc++.h>
#define N 100005
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,m,t[N],b[N];
ll A,B,C;
ll ans,x,xx,maxx;//x为当前剩余有怒气的人数maxx为枚举起点 
ll lit,lar;//比i小的时间总量,比i大的时间总量 
ll l,las;//比i小的b个数

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
void solveC()//C特别大,直接全部降低 
{
	ll minn=t[1];
	lit=lar=0;
	for(int i=1;i<=m;++i)
	{
		if(b[i]>minn) lar+=(b[i]-minn);
		if(b[i]<minn) lit+=(minn-b[i]);
	}
	if(B<=A) ans=lar*B;//直接降低时间不亏 
	else
	{
		if(lar>=lit) ans=lit*A+(lar-lit)*B;
		else ans=lar*A;
	}
	cout<<ans<<endl;
}

int main()
{
	//init
	read(A);read(B);read(C);
	read(n);read(m);
	for(int i=1;i<=n;++i) read(t[i]);
	sort(t+1,t+n+1);
	for(int i=1;i<=m;++i)
	{
		read(b[i]);
		maxx=Max(maxx,b[i]);
	}
	sort(b+1,b+m+1);
	
	//特判 
	if(C==10000000000000000LL)//C==1e16
	{
		solveC();
		return 0;
	}
	
	//solve
	las=0; ans=1000000000000000LL;
	for(int i=1;i<=n;++i) if(t[i]<maxx) {las=i;++x;xx+=C*(maxx-t[i]);};//xx为等待代价 
	for(int i=1;i<=m;++i) if(b[i]<maxx) {++l;lit+=(maxx-b[i]);} 
	for(int i=maxx;i>=1;--i)//最小的时间 
	{
		while(las&&t[las]>=i)//少一个发怒的人 
		{
			--las;
			--x;
		}
		ll nowans=xx;
		//下面计算调整代价
		
		while(l&&b[l]==i) --l;//少一个小于i的时间 
		if(B<=A) nowans+=B*lar;//全选B 
		else
		{
			if(lar>=lit) nowans+=lit*A+(lar-lit)*B;
			else nowans+=lar*A;
		}
		xx-=C*x;//时间少1 
		lit-=l;
		lar+=(m-l);
		ans=Min(ans,nowans);
	}
	cout<<ans<<endl;
	return 0;
}
/*
3 5 4
5 6
1 1 4 7 8
2 3 3 1 8 2
*/

Day1T2 相逢是问候

想到了欧拉定理,但在递归求解时想错了,以为每次都% \(\varphi(mod)\)...于是在此基础上暴力也写挂了直接100->50->30

Sol


Day1T3 组合数问题

这么水的题考场上居然只打了60分暴力。。。

Sol

发现每一项的共性,即\(r≡i*k+r (mod\) \(k)\)

\(f[i][j]\)表示\(\Sigma{C_{i}^{t}},(t\%k=j)\)

有递推式 \(f[i][j]=f[i-1][j]+f[i-1][j-1]\)
由于是模运算,边界要特判。显然要矩阵加速

\(ans=\)\(\begin{bmatrix} 1 & 0 & ... & 0 & 1\\ 1 & 1 & ... & 0 & 0 \\ .. & ... & ... & ... & ... \\ 0 & 0 & ... & 1 & 0 \\ 0 & 0 & ... & 1 & 1 \\ \end{bmatrix} ^{nk}\)\(\begin{bmatrix} 1\\ 0\\ 0\\ 0\\ 0\\ \end{bmatrix}\)

Code

#include<bits/stdc++.h>
#define N 1000005
#define re register
using namespace std;
typedef long long ll;
ll n,k,r;
ll mod;

struct Matrix
{
	int x,y;
	ll a[60][60];
	Matrix(){x=y=0;memset(a,0,sizeof(a));}
	Matrix operator * (const Matrix c)const
	{
		Matrix ret;
		ret.x=x; ret.y=c.y;
		for(re int i=0;i<x;++i)
			for(re int j=0;j<c.y;++j)
				for(re int k=0;k<y;++k)
					ret.a[i][j]=(ret.a[i][j]+a[i][k]*c.a[k][j]%mod)%mod;
		return ret;
	}
}one,R;

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

Matrix quickpow(Matrix a,ll b)
{
	Matrix ret;
	ret.x=ret.y=a.x;
	for(int i=0;i<a.x;++i) ret.a[i][i]=1;
	while(b)
	{
		if(b&1) ret=ret*a;
		a=a*a;
		b>>=1;
	}
	return ret;
}
int main()
{
//	freopen("problem.in","r",stdin);
//	freopen("problem.out","w",stdout);
	read(n);read(mod);read(k);read(r);
	R.x=1; R.y=k; R.a[0][0]=1;
	one.x=one.y=k;
	for(int i=0;i<k;++i)
	{
		one.a[i][i]++;
		one.a[(i-1+k)%k][i]++;
	}
	R=R*quickpow(one,n*k);
	cout<<R.a[0][r]<<endl;
	return 0;
}

Day2T1 摧毁“树状图”

不会,待填坑(然而考场上十几分钟就可以骗到36分为什么要花几个小时去打正解呢)

Day2T2 分手是祝愿

开始有点懵逼,快收卷的时候想多拿点分于是就看到了\(k==n\),发现可以水50,结果不小心水了80....实际上的结论很简单但不很容易看出来,说不定用心做一做可以出来(期望恐惧症)

Sol

由于每个开关最多按一次,所以最多n次可以关掉所有灯,然后又发现题目保证了\(k==n\)有50分,这意味着有50分不用期望,只是让我们求一个最佳方案,我们先解决一下这个问题

一个开关只会影响到比它小的其他灯,也就是说,对于最右边的那个亮着的灯来说,关掉它的方法就是直接按它的开关,于是从右向左枚举每一个灯,如果它亮着的就按一次开关,可以求出最佳点击次数\(cnt\)

根据上面的分析,直接输出\(cnt*n! \%mod\)即可获得\(50pts\)(然而强力的数据竟然给了\(80pts\)

发现一个开关对于灯泡状态的影响是其他开关任意组合都凑不出来的,也就是说,使所有灯都关掉的开关按法并不是多种,而是唯一的,即有些开关必须按,其他的开关不能按,于是我们设\(f[i]\)表示当前还有\(i\)个必须按的开关,要将它变成\(i-1\)个必须按的开关需要的期望步数,状态转移方程如下:

\(f[i]=\frac{i}{n}+(f[i]+f[i+1]+\frac{n-i}{n})\)

这个方程的意思是,要么按到了应该按的开关,要么按错了一个开关,之后还要把它按回来

\(ans=cnt,(cnt\leq k)\)

\(ans=f[k+1]+f[k+2]+...f[cnt]+k,(cnt>k)\)

Code

#include<bits/stdc++.h>
#define N 100005
using namespace std;
typedef long long ll;
const ll mod = 100003;
int n,a[N];
ll ans=1,cnt,k,f[N];

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

ll quickpow(ll a,ll b)
{
	ll ret=1;
	while(b)
	{
		if(b&1) ret=ret*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ret;
}
int main()
{
//	freopen("trennen.in","r",stdin);
//	freopen("trennen.out","w",stdout);
	read(n);read(k);
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=1;i<=n;++i) ans=ans*i%mod;
	cnt=0;
	for(int i=n;i>=1;--i)
	{
		if(a[i])//once
		{
			++cnt;
			for(int j=1;j*j<=i;++j)
			{
				if(i%j==0)
				{
					a[j]^=1;
					if(j*j!=i) a[i/j]^=1;
				}
			}
		}
	}
	if(cnt<=k) cout<<ans*cnt%mod<<endl;
	else
	{
		f[n+1]=0;
		for(int i=n;i>=1;--i) f[i]=((n-i)*f[i+1]+n)%mod*quickpow(i,mod-2)%mod;
		for(int i=k+1;i<=cnt;++i) k=(k+f[i])%mod;
		cout<<ans*k%mod<<endl;
	}
	return 0;
}

Day2T3 寿司餐厅

最小割?一建图脑子就乱,连边连了一年

Sol

WTCL,全场都吊打我

posted @ 2019-09-19 19:52  擅长平地摔的艾拉酱  阅读(210)  评论(0)    收藏  举报
/*取消选中*/