写在前面
这次的模拟赛,又爆炸了……(为什么自己每次都会有很多的失误,有点难过。)
这次出现的问题:
- 第一题中,注意在做除法的时候前面的被除数是不可以进行取模的操作,因为除法的取模是不满足同模原理的,要注意什么时候做除法;
- 第一题中,注意如果是两个longlong在取模之后也可能是会爆掉longlong的,所以我们要用大整数乘法。
- 第二题中,我的sort 死掉了。注意sort是左闭右开的操作,所以,当你要排序[l,r]中间的数字的时候,sort中应该是
sort(a+l,a+r+1);
; - 在第三题的时候,思路错误。自己想的是求出次短路,但是实际上,——<>——最短路和次短路之间是可能有相同的边的,那么当我删掉这条共同的边的同时,最短路和次短路就都是不合法的答案。
调整自己的状态,加油!
正文
第三题好毒瘤,至今不会……
第一题
求和
【问题描述】
一个无限大的二维矩阵d,满足 d i , j = i + j − 1 d_i,j=i+j-1 di,j=i+j−1。再给定 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 x1,y1,x2,y2,你需要求出矩阵d在以 x 1 , y 1 x_1,y_1 x1,y1为左上角、 x 2 , y 2 x_2,y_2 x2,y2为右下角的矩形内的元素和,即需要求出 ∑ i = x 1 x 2 ∑ i = y 1 y 2 d i , j \sum^{x2}_{i=x1}\sum^{y2}_{i=y1}d_i,j ∑i=x1x2∑i=y1y2di,j的值。为了避免大规模的输出,你只需要输出答案对一个给定的大整数m取模后的结果。
【输入文件】
一行五个正整数 x 1 , y 1 , x 2 , y 2 , m x_1,y_1,x_2,y_2,m x1,y1,x2,y2,m
【输出文件】
输出一个整数,为所求的答案对m取模后的结果。
【输入输出样例】
square.in
2 1 5 3 10007
square.out
54
【数据规模和约定】
对于20%的数据,满足 x 2 , y 2 , m ≤ 1000 x_2,y_2,m≤1000 x2,y2,m≤1000。
对于40%的数据,满足 x 2 , y 2 , m ≤ 100000 x_2,y_2,m≤100000 x2,y2,m≤100000。
对于70%的数据,满足 x 2 , y 2 , m ≤ 1 0 9 x_2,y_2,m≤10^9 x2,y2,m≤109。
对于100%的数据,满足 1 ≤ x 2 , y 2 , m ≤ 1 0 18 , 1 ≤ x 1 ≤ x 2 , 1 ≤ y 1 ≤ y 2 1≤x_2,y_2,m≤10^{18},1≤x_1≤x_2,1≤y_1≤y_2 1≤x2,y2,m≤1018,1≤x1≤x2,1≤y1≤y2。
题解
【简要题解】
首先,我们可以把问题转化为求一个左上角为1,1,左下角为n,m的子矩形的和,通过差分即可完成这一步操作。
容易推出,上面的问题的答案为
(
n
×
m
×
(
n
+
m
)
)
2
\frac{(n×m×(n+m))}{2}
2(n×m×(n+m))。将公式代入即可得到70%的分数。
如下图:
n行
{
1
2
3
⋯
1
+
n
−
1
2
3
4
⋯
2
+
n
−
1
⋮
⋮
n
n
+
1
n
+
2
⋯
m
+
n
−
1
⏟
m项
\text{n行}\begin{matrix}\underbrace{ \left\{ \begin{matrix}1&2&3&\cdots&1+n-1\\2&3&4&\cdots&2+n-1\\\vdots&&&&\vdots\\n&n+1&n+2&\cdots&m+n-1\end{matrix}\right. }\\\text{m项}\end{matrix}
n行
⎩⎪⎪⎪⎨⎪⎪⎪⎧12⋮n23n+134n+2⋯⋯⋯1+n−12+n−1⋮m+n−1m项
我们可以发现,对于相对的数字加起来的值是定值(m+n),一共有 m ∗ n 2 \frac{m*n}{2} 2m∗n个数字,所以最后的答案就是 ( n × m × ( n + m ) ) 2 \frac{(n×m×(n+m))}{2} 2(n×m×(n+m))。
对于最后30%,乘法操作会超出2^64,不能直接相乘取模。我们可以使用高精度来解决这个问题,也可以把乘法操作看作很多次加法操作,利用快速幂的思想予以解决(大整数乘法)。
第二题
分组配对
【问题描述】
在一个班级中,恰好有n名男生和n名女生。为了便于管理,老师给这n名男生和n名女生分别从1到n进行编号。
某一天,老师在班级里开展了一项游戏。这个游戏需要将班级里的同学分成若干个不同的小组,且每个小组中需要同等数量的男生和女生。每个小组中,每一名男生需要和一名同小组的女生进行配对搭档,来完成游戏中的任务。一名男生不能和多名女生进行配对,一名女生也不能和多名男生进行配对。
老师决定采用一种比较简便的方式对全班同学进行分组:按编号顺序进行划分。具体来说,老师会确定若干个区间 [ l 1 , r 1 ] , [ l 2 , r 2 ] , … , [ l k , r k ] [l_1,r_1 ],[l_2,r_2 ],…,[l_k,r_k ] [l1,r1],[l2,r2],…,[lk,rk],满足 l 1 = 1 , r k = n , l i = r ( i − 1 ) + 1 ( 1 < i ≤ k ) l_1=1,r_k=n,l_i=r_(i-1)+1(1<i≤k) l1=1,rk=n,li=r(i−1)+1(1<i≤k),编号处于同一个区间的男生和女生分为一组。
为了让游戏保证尽量公平,老师希望没有哪一个小组的实力过于突出。具体来说,每名同学都有一个正整数实力值,用来衡量一名同学的能力大小。当一名实力值为a和一名实力值b的女生进行配对时,这一对同学的实力值就会是a×b。而一个小组的综合实力就是这个小组中每一对同学的实力值的和。为了保证不出现综合实力过高的小组,老师希望在分组后,不论小组内如何配对,每个小组的综合实力都不会超过一个给定值M。
最后,为了缩短游戏进行的时间,老师希望所分的小组数量尽可能少。你需要帮助他求出:在满足上述分组规则的情况下,最少的小组数量是多少?
【输入文件】
输入文件第一行为两个正整数n,M。
接下来两行,第一行为n个正整数 a 1 a n a_1~a_n a1 an,其中 a i a_i ai表示编号为i的男生的实力值;第二行为n个正整数 b 1 b n b_1~b_n b1 bn,其中 b i b_i bi表示编号为i的女生的实力值。
【输出文件】
输出一个整数,为最少的小组数量。输入数据保证至少存在一种满足分组规则的分组方式。
【输入输出样例】
pair.in
3 50
6 7 2
6 3 5
pair.out
2
【数据规模和约定】
对于10%的数据,满足n≤10。
对于30%的数据,满足n≤1000。
对于50%的数据,满足n≤10000。
对于70%的数据,满足n≤100000。
对于100%的数据,满足 1 ≤ n ≤ 500000 , 1 ≤ a i , b i ≤ 100000 , 1 ≤ M ≤ 1 0 15 1≤n≤500000,1≤a_i,b_i≤100000, 1≤M≤10^{15} 1≤n≤500000,1≤ai,bi≤100000,1≤M≤1015。
题解
首先,容易发现一个小组内的最优配对方式(能得到最大综合实力的方式)为:实力值最大的男生和最大的女生配对,次大的和次大的配对,以此类推。
接着,容易发现:在小组内增加一男一女,小组的最大综合实力值一定上升。以此我们容易得到一种贪心的分组方式:从编号1的人开始向后考虑,尽可能把人放进同一组,直到放下一对男生女生后最大综合实力值会超过M。用如此方法确定第一组,然后类似地确定第二组、第三组,直到分到最后一组。
一个比较麻烦的问题是,在小组内增加一男一女后,最大综合实力值并不方便快速统计。这意味着增量法不能使用。因此,我们可以用倍增+二分的方式来处理这个问题。对于每一组,考虑这组大小为 1 , 2 , 4 , … , 2 p 1,2,4,…,2^p 1,2,4,…,2p时,最大综合实力值都没有超过M,但组大小为 2 ( p + 1 ) 2^{(p+1)} 2(p+1)时最大综合实力值会超过M,那么这组的最大大小应该在 2 p 2 ( p + 1 ) − 1 2^p~2^{(p+1)-1} 2p 2(p+1)−1内。在此范围内进行二分即可。这样,当实际组大小是k时,时间复杂度应为 O ( k l o g 2 k ) O(klog^2 k) O(klog2k)。则总时间复杂度不超过 O ( n l o g 2 n ) O(nlog^2 n) O(nlog2n)。
补充:由于这道题中的二分会被卡,(如果我构造一组数据是每一个都是长度为1的区间,那么,此时的二分还不如枚举……),所以,我们可以考虑倍增加速。
第三题
不会……
城市游戏
【问题描述】
小A和小B正在进行一个游戏。这个游戏开展于一个城市中。城市中总共包含n个路口,从1到n进行编号。某些路口之间连接着具有一定长度的双向道路,道路总共有m条。
小A正在这个城市的1号路口,想要移动到n号路口。小B的目标是尽可能阻止小A。他的阻止方法是:在小A位于某个路口上时,永久地封闭城市中的一条道路。
如果小B使用封闭道路的权力只有一次,你需要求出:在此基础上,如果小A和小B都使用最佳策略(即小A希望他走到n号路口的总距离尽量小,小B希望小A的移动总距离尽量大),小A走到n号路口所需要移动的总距离是多少?
【输入文件】
第一行,两个整数n,m。
接下来m行,每行三个整数u,v,l,描述了一条道路连接的两个路口的编号以及道路的长度。
【输出文件】
输出一行一个整数,为所求的答案。若小A不能到n达号点,输出-1。
【输入输出样例】
city.in
4 4
1 2 1
2 3 1
3 4 1
1 4 1
city.out
3
【数据规模和约定】
对于20%的数据,满足n≤200,m≤1000
对于40%的数据,满足n≤1000,m≤2000
对于70%的数据,满足n≤3000,m≤10000
对于100%的数据,满足1≤n≤100000,1≤m≤200000,道路长度为不超过10^9的正整数。
有一个错误的算法:求出次短路。
code
第一题
这一个代码,并不是用的上面的结论,而是自己在考场上推出来的,在规定的矩阵中,可以把每一行的数字用等差数列累加,之后再求出每一行的累加。
等差数列求和的公式:
S
=
a
1
∗
n
+
n
∗
(
n
−
1
)
2
S=a1*n+\frac{n*(n-1)}{2}
S=a1∗n+2n∗(n−1)
注意:对于除以2而言,我们要特殊处理。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll x1, x2, s1, s2, MOD;
ll sum1, sum2, sum;
ll mul(ll a,ll b)
{
ll ans=0;
while(b)
{
if(b&1) ans=(ans+a)%MOD;
a=(a+a)%MOD;
b>>=1;
}
return ans%MOD;
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%lld%lld%lld%lld%lld",&x1,&s1,&x2,&s2,&MOD);
ll dx=x2-x1+1, dy=s2-s1+1;/*求出行数和列数,因为后面有/2,所以不可以在这里取模*/
if(dy%2==0) sum1=mul(dx, mul(dy/2, dy-1))%MOD;
else sum1=mul(dx, mul(dy, (dy-1)/2))%MOD;
sum=(mul(x1+s1-1, dx))%MOD;/*a1*n*/
if(dx%2==0) sum2=(mul(dy, sum%MOD+mul(dx/2, dx-1)))%MOD;
else sum2=(mul(dy, sum%MOD+mul(dx, (dx-1)/2)))%MOD;
sum=(sum1%MOD+sum2%MOD)%MOD;
printf("%lld\n",sum%MOD);
return 0;
}
第二题
#include<bits/stdc++.h>
using namespace std;
const int nn=500003;
typedef long long ll;
int n, a[nn], b[nn], cnt=0;
int c[nn], d[nn];
ll M;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
inline bool cmp(int a,int b) {return a>b;}
inline bool check(int l,int r)
{
ll sum=0;
for(int i=l;i<=r;++i) c[i]=a[i], d[i]=b[i];
sort(c+l,c+r+1,cmp), sort(d+l,d+r+1,cmp);/*注意是左闭右开的区间*/
for(int i=l;i<=r;++i)
{
sum+=(ll)c[i]*d[i];
if(sum>M) return false;
}
return true;
}
int main()
{
freopen("pair.in","r",stdin);
freopen("pair.out","w",stdout);
scanf("%d%lld",&n,&M);
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i) b[i]=read();
int L=1, R=0;
while(R<n)
{
int p=1;
for(;L+p<n;p*=2)/*倍增的是区间的长度*/
if(!check(L,L+p)) break;/*此时出来的p是不合法的*/
int l=L+p/2, r=L+p, mid;
while(l<r)
{
mid=(l+r+1)>>1;
if(check(L,mid)) l=mid;
else r=mid-1;
}
R=l, cnt++;
L=R+1;
}
cout<<cnt<<endl;
return 0;
}