2019/8/20 校内模拟赛 考试报告
A.特工(\(\text{agent.cpp}\))
题目描述
IMF有\(n\)个Agent,每个Agent的能力值互不相同,现在部长先生想要派出\(A,B\)两队Agent去参加特别任务.但是参加大战的两个队伍要满足两个要求:
-
\(A\)队中能力最大的Agent的能力值要小于\(B\)队能力最弱的Agent的能力值.
-
\(A,B\)两队都要有人参加.
并不是所有的Agent都要去参加的,心急的部长先生想知道有多少种安排Agent的方案.由于答案可能很大,所以只需要你求出答案模\(10^9+7\)的值就可以了.
输入格式
输入仅一行,为一个整数\(n\).
输出格式
输出答案模\(10^9+7\)的值.
数据范围
\(n\le 10^9\)
题意
从\(1...n\)这\(n\)个数选出若干个分为两个集合\(A,B\),要求两个集合都不为空,且集合\(A\)的数都小于集合\(B\)里的数,求总方案数.
思路\(1\):\(O(n)\)
枚举集合\(A\)中的最大数,设为\(i\),那么:
\([1,i-1]\)内的所有数可以任意选择加入或不加入集合\(A\),方案数\(2^{i-1}\).
\([i+1,n]\)内的所有数可以任意选择加入或不加入集合\(B\),但是不能都不加入集合\(B\),方案数\(2^{n-i}-1\).
容易证明这样的枚举不重复地计算了所有情况.
答案即为\(\sum_{i=1}^n2^{i-1} \times (2^{n-i}-1)\).
思路\(2\):\(O(logn)\)
考虑对上式进行化简.
考场上我采用了数列求和的乘比错位相减法,记\(S=\sum_{i=1}^n2^{i-1} \times (2^{n-i}-1)\),用\(2S-S\)化简得出答案.
实际上可以直接化简,将\(2^{i-1}\)乘进括号,用等比数列求和公式得到答案.
最终答案为\((n-2)2^{n-1}+1\).用快速幂在\(O(logn)\)的复杂度内就可以快速计算.
B.小奇回地球\((earth.cpp)\)
二分调节的速度,用\(SPFA\)判断最短路长度是否非负.
注意:
- 需要删掉无法到达\(n\)点的点,否则可能会因为这部分点中存在负环而错判不存在最短路.
- 直接\(SPFA\)超时,
我加了\(SLF\)优化成功卡过.
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int SIZE=20005,INF=0x3F3F3F3F;
int T,n,m,head[SIZE],nex[SIZE],ver[SIZE],edge[SIZE],Tot,Dis[SIZE],inTim[SIZE];
bool Flag[SIZE],inq[SIZE],vis[SIZE];
deque<int>q;
void Ins(int u,int v,int e,bool FF)
{
nex[++Tot]=head[u];head[u]=Tot;
ver[Tot]=v;edge[Tot]=e;Flag[Tot]=FF;
}
void Init()
{
for(int i=1;i<=n;++i)
vis[i]=head[i]=0;
Tot=1;
}
bool SPFA()
{
for(int i=1;i<=n;++i)
{
Dis[i]=INF;
inTim[i]=inq[i]=0;
}
Dis[1]=0;
while(q.size())q.pop_front();
q.push_back(1);
inq[1]=1;
++inTim[1];
while(q.size())
{
int u=q.front();
q.pop_front();
inq[u]=0;
for(int i=head[u];i;i=nex[i])
{
int v=ver[i];
if(!vis[v]||Flag[i])continue;
if(Dis[v]>Dis[u]+edge[i])
{
Dis[v]=Dis[u]+edge[i];
if(!inq[v])
{
if(q.size()&&Dis[q.front()]>Dis[v])
q.push_front(v);
else q.push_back(v);
inq[v]=1;
++inTim[v];
if(inTim[v]==n)return 0;
}
}
}
}
return 1;
}
void Bound()
{
int Ans=INF,L=-100000,R=100000;
while(L<=R)
{
int Mid=(L+R)>>1;
for(int i=2;i<=Tot;i++)edge[i]+=Mid;
bool Flag=SPFA();
if(Flag&&Dis[n]>=0)
{
R=Mid-1;
Ans=min(Ans,Dis[n]);
}
else L=Mid+1;
for(int i=2;i<=Tot;i++)edge[i]-=Mid;
}
if(Ans==INF)Ans=-1;
printf("%d\n",Ans);
}
void DFS(int u)
{
if(vis[u])return;
vis[u]=1;
for(int i=head[u];i;i=nex[i])if(Flag[i])DFS(ver[i]);
}
int main()
{
scanf("%d",&T);
int u,v,e;
while(T--)
{
Init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&e);
Ins(u,v,e,0);
Ins(v,u,e,1);
}
DFS(n);
Bound();
}
return 0;
}
C.极光\((aurora.cpp)\)
转化为二维平面上的数点问题.
如果把题目中的曼哈顿距离改成切比雪夫距离,就是普通的二维平面带修改的矩形数点问题,即变形的三维偏序问题,可以用\(CDQ\)分治在\(O(nlog^2n)\)的复杂度内解决.
那么如何将曼哈顿距离转变为切比雪夫距离呢?
将一个点\((x,y)\)的坐标变为\((x+y,x-y)\)后,原坐标系中的曼哈顿距离\(=\)新坐标系中的切比雪夫距离.
将一个点\((x,y)\)的坐标变为\((\frac{x+y}2,\frac{x-y}2)\)后,原坐标系中的切比雪夫距离\(=\)新坐标系中的曼哈顿距离.
好了,现在这道题就是\(cdq\)分治裸题了,我们将第一维时间排序后分治,第二维横坐标\(x\)归并排序维护或直接\(sort\),第三维纵坐标\(y\)用树状数组维护就可以了.时间复杂度\(O(nlog^2n)\).
由于数据范围的锅,考场上\(\text{RE}\)了两个点,把偏移量和数组都开大一点就可以过了.😮
#include<cstdio>
#include<algorithm>
using namespace std;
const int SIZE=5000005;
int n,q,T[SIZE],A[SIZE],Tot,Time,qID,Ans[SIZE];
void change(int i,int x){i+=500000;for(;i<SIZE;i+=i&(-i))T[i]+=x;}
int query(int i){i+=500000;int x=0;for(;i;i-=i&(-i))x+=T[i];return x;}
struct Op
{
int Tim;
int tp;//0 修改-加入一个点
//1 查询-加上当前答案
//2 查询-减去当前答案
int X,Y;
int ID;
bool operator <(const Op &x)const
{
if(Tim!=x.Tim)return Tim<x.Tim;
if(X!=x.X)return X<x.X;
if(Y!=x.Y)return Y<x.Y;
return tp<x.tp;
}
}op[SIZE],Tem[SIZE];
void cdq(int L,int R)
{
if(L==R)return;
int Mid=(L+R)>>1,i,j,pos=L-1;
cdq(L,Mid);
cdq(Mid+1,R);
for(j=L,i=Mid+1;i<=R;i++)
{
while(op[j].X<=op[i].X&&j<=Mid)
{
if(op[j].tp==0)change(op[j].Y,1);
Tem[++pos]=op[j++];
}
if(op[i].tp==1)Ans[op[i].ID]+=query(op[i].Y);
if(op[i].tp==2)Ans[op[i].ID]-=query(op[i].Y);
Tem[++pos]=op[i];
}
for(int k=L;k<j;k++)
if(op[k].tp==0)
change(op[k].Y,-1);
while(j<=Mid)Tem[++pos]=op[j++];
for(int k=L;k<=R;k++)op[k]=Tem[k];
}
char SS[10];
int main()
{
//freopen("aurora.in","r",stdin);
//freopen("aurora.out","w",stdout);
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
++Time;
scanf("%d",&A[i]);
op[++Tot]=(Op){Time,0,i+A[i],i-A[i],0};
}
int x,k;
for(int i=1;i<=q;i++)
{
++Time;
scanf("%s%d%d",SS,&x,&k);
if(SS[0]=='M')
{
op[++Tot]=(Op){Time,0,x+k,x-k,0};
A[x]=k;
}
if(SS[0]=='Q')
{
++qID;
int XX=x+A[x];
int YY=x-A[x];
int AA=XX-k;
int BB=YY-k;
int CC=XX+k;
int DD=YY+k;
op[++Tot]=(Op){Time,1,CC,DD,qID};
op[++Tot]=(Op){Time,1,AA-1,BB-1,qID};
op[++Tot]=(Op){Time,2,AA-1,DD,qID};
op[++Tot]=(Op){Time,2,CC,BB-1,qID};
}
}
sort(op+1,op+1+Tot);
cdq(1,Tot);
for(int i=1;i<=qID;i++)printf("%d\n",Ans[i]);
return 0;
}