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;
}
posted @ 2019-08-20 15:54  TaylorSwift13  阅读(206)  评论(0编辑  收藏  举报