Codeforces Beta Round #10 题解

132 阅读1分钟

比赛链接

A

思路

i[1,n]\forall i\in [1,n],必定有rilir_i-l_i的时间段功率为P1P_1,之后对i[2,n]i\in [2,n]维护从第i1i-1段结束到第ii段开始产生的代价。设间隔时间为dis=liri1dis=l_i-r_{i-1}
T1T_1:两种情况。如果dis<T1dis<T_1则代价为dis×P1dis\times P_1;否则就是T1×P1T_1\times P_1。整合后为min(dis,T1)×P1min(dis,T_1)\times P_1
T2T_2:三种情况。如果dis<T1dis<T_1则代价为00;如果T1disT2T_1\leq dis\leq T_2则代价为(disT1)×P2(dis-T_1)\times P_2;否则就是T2×P2T_2\times P_2。整合后为max(0,min(disT1,T2))×P2;max(0,min(dis-T1,T2))\times P2;
T3T_3:两种情况。如果dis<T3dis<T_3则代价为00;否则代价为(disT1T2)×P3(dis-T_1-T_2)\times P_3。整合后为max(0,disT1T2)×P3max(0,dis-T_1-T_2)\times P_3

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
ll n,p1,p2,p3,t1,t2,ans;
int main()
{
	sf(n)sf(p1)sf(p2)sf(p3)sf(t1)sf(t2)
	ll lst=0,l,r;
	rep(i,1,n)
	{
		sf(l)sf(r)
		ans+=(r-l)*p1;
		if(i==1)
		{
			lst=r;
			continue;
		}
		ll dis=l-lst;
		ans+=min(dis,t1)*p1;
		ans+=max(0ll,min(dis-t1,t2))*p2;
		ans+=max(dis-t1-t2,0ll)*p3;
		lst=r;
	}
	pf(ans)
}

B

思路

由于NNKK都不大,每次询问的时候可以枚举每一个点作为这些人入座的左端点,依次向右坐,如果坐得开且这样入座的代价比之前任何方案都小,就把这个方案记录下来。查询是否能坐得开需要看这些座位上是否已经有人(复杂度O(K)O(K)),入座代价的计算需要枚举每一个人到中心点的距离(复杂度为O(K)O(K)),那么算法的复杂度为O(NK3)O(NK^3),显然是过不了的。

优化:
可以对每一行维护前缀和,位置上有人为11,否则为00。每次完成一次询问就进行维护。这样就可以在O(1)O(1)时间内查询以第ii行第jj列作为起点,长度为mm的区间内是否有人(sum[i][j+m1]sum[i][j1]==0sum[i][j+m-1]-sum[i][j-1]==0)。

入座的代价显然为等差数列,确定首项、末项、项数之后求和即可,复杂度同样为O(1)O(1)。 最终复杂度被优化为O(NK2)O(NK^2)

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const int K=101;
ll n,k;
ll sum[K][K],a[K][K];
void print()
{
	rep(i,1,k)
	{
		rep(j,1,k)
			cout<<a[i][j]<<" ";
		cout<<endl;
	}
	cout<<endl;
}
int main()
{
	sf(n)sf(k)
	ll cen=(k+1)>>1;
	while(n--)
	{
		ll m;
		sf(m);
		ll maxi=LONG_LONG_MAX,x=LONG_LONG_MAX,y=LONG_LONG_MAX;
		rep(i,1,k)
		{
			rep(j,1,k-m+1)
			{
				if(sum[i][j+m-1]-sum[i][j-1]==0)
				{
					ll val=abs(cen-i)*m;
					if(i==3 && j==1)
					{
						++val;
						--val;
					}
					if(cen<=j || cen>=j+m-1)
						val+=m*(abs(j-cen)+abs(j+m-1-cen))>>1;
					else
						val+=(cen-j+1)*abs(j-cen)/2+(j+m-1-cen)*(1+j+m-1-cen)/2;
					if(val<maxi)
					{
						maxi=val;
						x=i;
						y=j;
					}
				}
			}
		}
		if(maxi!=LONG_LONG_MAX)
		{
			printf("%lld %lld %lld\n",x,y,y+m-1);
			rep(l,y,y+m-1)
				a[x][l]=1;
			rep(l,y,k)
				sum[x][l]=sum[x][l-1]+a[x][l];
		}
		else
			printf("-1\n");
	}
}

C

思路

数根其实是一个数对99取模,如果结果是00,就改成99。所以d(x)=(x1)%9+1d(x)=(x-1)\%9+1。考虑用容斥原理做。要求ABCA\cdot B\neq Cd(C)=d(d(A)d(B))d(C)=d(d(A)\cdot d(B))的三元组个数,可以先求出d(C)=d(d(A)d(B))d(C)=d(d(A)\cdot d(B))的个数再减去AB=CA\cdot B=C的个数。因为如果AB=CA\cdot B=C,根据函数dd的定义,该三元组必定满足d(C)=d(d(A)d(B))d(C)=d(d(A)\cdot d(B))

求满足AB=CA\cdot B=C的三元组个数,考虑对于i[1,N]i\in [1,N]有多少数字和ii相乘可以得到一个比NN小的数字,这就可以理解为NN内最多有多少个ii,因此答案就是Σi=1NNi\Sigma_{i=1}^{N}\frac{N}{i}

接下来就是求满足d(C)=d(d(A)d(B))d(C)=d(d(A)\cdot d(B))的三元组个数,根据数根特点可知d(x)[1,9]d(x)\in[1,9]。考虑分别用iijj枚举d(A)d(A)d(B)d(B),可知d(C)=(i×j1)%9+1d(C)=(i\times j-1)\%9+1。因此,只要我们知道[1,N][1,N]中有多少数字的数根为d(A)d(A)d(B)d(B)d(C)d(C)。根据排列组合就可以算出三元组的个数。考虑使用大小为1010的桶(tt)进行预处理,答案就是t[i]×t[j]×t[(i×j1)%9+1]t[i]\times t[j]\times t[(i\times j-1)\%9+1]

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const int N=1E6+10;
int n;
ll ans;
ll t[10];
int main()
{
	cin>>n;
	rep(i,1,n)
	{
		++t[(i-1)%9+1];
		ans-=n/i;
	}
	rep(i,1,9)
	{
		rep(j,1,9)
		{
			ans+=t[i]*t[j]*t[(i*j-1)%9+1];
		}
	}
	cout<<ans<<endl;
}

D

思路

f[i][j]f[i][j]作为以AA中第ii个元素作为结尾,BB中第jj个元素作为结尾得到的LCISLCIS长度。
A[i]B[j]A[i]\neq B[j]f[i][j]=0f[i][j]=0
A[i]=B[j]A[i]=B[j]f[i][j]=max(f[x][y]  x[1,i1],y[1,j1],A[x]<A[j])+1f[i][j]=max(f[x][y]\ |\ x\in[1,i-1],y\in [1,j-1],A[x]<A[j])+1
很明显这是一个O(N4)O(N^4)的算法,但是它就是过了。因为只有在A[i]=B[j]A[i]=B[j]的时候才会展开内部两层循环,所以只要不出很长一段元素都相等的数据这个算法也可以跑得很快。
优化:设c[j]c[j]作为以BB中第jj个元素作为结尾得到的LCISLCIS长度,在枚举jj时:
如果A[i]>B[j]A[i]>B[j],那么之后如果遇到A[i]=B[j]A[i]=B[j'],就可以把这两个相等的元素接到以B[j]B[j]作为结尾的LCISLCIS后面,所以用c[j]c[j]维护当前得到的不包含A[i]A[i]LCISLCIS长度(记为maximaxi)。
如果A[i]=B[j]A[i]=B[j],就把这两个元素接到之前维护好的LCISLCIS后面,得到一个包含A[i]A[i]LCISLCIS长度(即maxi+1maxi+1),并以此维护c[j]c[j]
最终复杂度降为O(n2)O(n^2)

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const int N=505;
ll n,m,ans;
ll a[N],b[N],dp[N][N],pre[N],f[N],mp[N];
void dfs(int cur)
{
	if(!cur)
		return;
	dfs(pre[cur]);
	printf("%lld ",a[cur]);
}
void print()
{
	rep(i,1,n)
	{
		rep(j,1,m)
		{
			cout<<dp[i][j]<<" ";
		}
		cout<<endl;
	}
}
int main()
{
	sf(n);
	rep(i,1,n)
		sf(a[i])
	sf(m)
	rep(i,1,m)
		sf(b[i])
	int pos=-1;
	rep(i,1,n)
	{
		ll maxi=0;
		int lst=0;
		rep(j,1,m)
		{
			if(a[i]==b[j])
			{
				dp[i][j]=maxi+1;
				f[j]=maxi+1;
				pre[i]=lst;
				mp[j]=i;
			}
			else if(a[i]>b[j] && maxi<f[j])
			{
				maxi=f[j];
				lst=mp[j];
			}
			if(dp[i][j]>ans)
			{
				ans=dp[i][j];
				pos=i;
			}
		}
	}
	if(ans==0)
	{
		cout<<0<<endl;
		return 0;
	}
	pf(ans)
	dfs(pos);
}