A
思路
对,必定有的时间段功率为,之后对维护从第段结束到第段开始产生的代价。设间隔时间为。
:两种情况。如果则代价为;否则就是。整合后为。
:三种情况。如果则代价为;如果则代价为;否则就是。整合后为
:两种情况。如果则代价为;否则代价为。整合后为。
代码
#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
思路
由于和都不大,每次询问的时候可以枚举每一个点作为这些人入座的左端点,依次向右坐,如果坐得开且这样入座的代价比之前任何方案都小,就把这个方案记录下来。查询是否能坐得开需要看这些座位上是否已经有人(复杂度),入座代价的计算需要枚举每一个人到中心点的距离(复杂度为),那么算法的复杂度为,显然是过不了的。
优化:
可以对每一行维护前缀和,位置上有人为,否则为。每次完成一次询问就进行维护。这样就可以在时间内查询以第行第列作为起点,长度为的区间内是否有人()。
入座的代价显然为等差数列,确定首项、末项、项数之后求和即可,复杂度同样为。 最终复杂度被优化为。
代码
#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
思路
数根其实是一个数对取模,如果结果是,就改成。所以。考虑用容斥原理做。要求且的三元组个数,可以先求出的个数再减去的个数。因为如果,根据函数的定义,该三元组必定满足。
求满足的三元组个数,考虑对于有多少数字和相乘可以得到一个比小的数字,这就可以理解为内最多有多少个,因此答案就是。
接下来就是求满足的三元组个数,根据数根特点可知。考虑分别用和枚举和,可知。因此,只要我们知道中有多少数字的数根为、、。根据排列组合就可以算出三元组的个数。考虑使用大小为的桶()进行预处理,答案就是。
代码
#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
思路
设作为以中第个元素作为结尾,中第个元素作为结尾得到的长度。
若,。
若,。
很明显这是一个的算法,但是它就是过了。因为只有在的时候才会展开内部两层循环,所以只要不出很长一段元素都相等的数据这个算法也可以跑得很快。
优化:设作为以中第个元素作为结尾得到的长度,在枚举时:
如果,那么之后如果遇到,就可以把这两个相等的元素接到以作为结尾的后面,所以用维护当前得到的不包含的长度(记为)。
如果,就把这两个元素接到之前维护好的后面,得到一个包含的长度(即),并以此维护。
最终复杂度降为。
代码
#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);
}