7.11总结

7.11总结

得分情况

估分:100+50+100

实际:100+100+50

Rank 10

第三题SPFA写挂啦,幹

本来可以AK的

T1

题目大意

在七山七海之外的一个小村庄,白雪公主与N个矮人住在一起,所有时间都花在吃和玩League of Legend游戏。白雪公主决心终结这样的生活,所以为他们举办了体育课。 在每节课开始时,矮人必须按他们的身高站队。假定矮人们有高度1,2,...,N(每个人高度互不相同)。然而,由于不健康的生活方式,矮人的智力有所恶化,所以他们没有能力依照自己的高度排序。

因此,白雪公主发出以下形式命令帮助他们:

1 X Y:X和Y位置的矮人互换位置。

2 A B:询问高度为A,A+1,..., B的矮人(不一定是按照这个顺序)是否已形成了当前队列的连续子序列。

帮助矮人按照白雪公主的指示行动,并回答她的问题。

2≤N≤200,000,2≤M≤200,000。

感觉以前做过类似的题。

询问是否A~B的数是否形成连续子序列,不一定按顺序,这类询问的基本套路就是算出A~B中所有数的最靠左的位置和最靠右的位置。这题只有单点修改,打一棵权值线段树,注意身高和位置的转换就可以了。

T2

你是M,一个雇佣N个标号为从1到N的间谍的情报机关的总管。每个间谍被派往不同的国家并在那获取重要情报。

如下是你的任务:

​ 1.在部分间谍间组织会面。每次会面在两个间谍间进行,两个间谍交换他们自己获取的或从之前会面中得到的信息。因为在不同国家的两个间谍间组织机密会面很困难,所以每次秘密会面都有一个费用。

​ 2.当所有会面结束后,选择一部分间谍参加拯救世界的任务。一个间谍k参加此项任务需要花费Mk。很重要的一点是,任务要成功,必须满足参加任务的间谍获取的情报聚集到一起包含了其他剩余间谍所有的信息。

请找出完成任务的最小花费,即组织会面和派遣间谍的费用之和。

2≤N≤1000

正解

最小生成树。每个节点向n+1连一条权值为Mk的边。跑一遍kruskal就好了

我的做法

然而我并没有这样做。设一条边连接的两个连通块中派遣的代价最小的分别为mi1,mi2,边权为cost。若cost+min(mi1,mi2)(leq)mi1+mi2则连边。

这样很假,但也水过去了

T3

在遥远的未来,行星之间的食品运输将依靠单向的贸易路线。每条路径直接连接两个行星,且其运输时间是已知的。

贸易商协会打算利用一项最近发现的新技术——超空间旅行,以增加一些新的航线。通过超空间旅行的航线也是单向的。由于该项技术仍处于试验阶段,超空间旅行的时间目前是未知的,但它不取决于行星之间的距离,所以每个超空间旅行的路线将花费等量的时间。

下图是三个相互联通的行星及其运输时间的例子。行星使用正整数标号,超空间旅行时间记为“x”(图片对应第二个输入样例):

![](D:黎奕欣总结7.11总结.assetsThu, 11 Jul 2019 145233.png)

运输时间以天计,并且始终是一个正整数。

贸易商协会希望对引进新航线的结果进行分析:对于某两个行星A和B,他们想知道对于任意的x,从A到B的最短路径的总运输时间的所有可能的值。例如,在上述情况中,从星球2到星球1的最短路径所需时间可以取值5(如果x≥5),4,3,2,或1天(如果x<5)

设dis[x][s]表示从A到x,经过s次超空间路线,普通路线的最小花费。用SPFA可以完成然而我SPFA打挂了

接下来,把dis[B][s]取出来,做一遍类似斜率优化的过程,找出有哪些可能的形如kx+b的式子可以被加进答案里。

然后就可以计算答案了。

细节超级多,自己注意

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct qy
{
	long long x,y,cost;
};

long long n,m,i,j,k,x,y,cost,Q,A,B,bzz,ll,rr,sum1,sum2,lastll,lastsum;
qy l[20005];
long long next[20005],last[505],tot;
long long dis[505][505];
long long list[1000005],head,tail,bz[505];
long long path[505][2];

long long read()
{
	long long s=0; char ch=getchar();
	while (((ch<'0')||(ch>'9'))&&(ch!='x')) ch=getchar();
	if (ch=='x')
	return -1;
	while ((ch>='0')&&(ch<='9'))
	{
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s;
}

void insert(long long x,long long y,long long z)
{
	tot++;
	l[tot].x=x;l[tot].y=y;l[tot].cost=z;
	next[tot]=last[x];
	last[x]=tot;
}

double xl(double x1,double y1,double x2,double y2)
{
	return ((y2-y1)/(x2-x1));
}

long long lower(double x)//下取整
{
	x+=0.000000001;
	long long t=x;
	return t;
}

long long upper(double x)//上取整
{
	x-=0.000000001;
	long long t=x;
	return t+1;
}

long long js(long long x,long long k,long long b)
{
	return x*k+b;
}

int main()
{
	freopen("read.in","r",stdin);
	scanf("%lld%lld",&n,&m);
	for (i=1;i<=m;i++)
	{
		scanf("%lld%lld",&x,&y);
		cost=read();
		insert(x,y,cost);
	}
	scanf("%lld",&Q);
	while (Q--)
	{
		scanf("%lld%lld",&A,&B);
		memset(bz,0,sizeof(bz));
		memset(dis,120,sizeof(dis));
		for (i=0;i<=n;i++)
		dis[A][i]=0;
		list[1]=A;head=0;tail=1;
		bz[A]=1;
		while (head<tail)
		{
			x=list[++head];
			for (i=last[x];i>=1;i=next[i])
			{
				y=l[i].y;cost=l[i].cost;bzz=0;
				if (cost==-1)
				{
					for (j=0;j<=n;j++)
					{
						if (dis[y][j+1]>dis[x][j])
						{
							bzz=1;
							dis[y][j+1]=dis[x][j];
						}
					}
				}
				else
				{
					for (j=0;j<=n;j++)
					{
						if (dis[y][j]>dis[x][j]+cost)
						{
							bzz=1;
							dis[y][j]=dis[x][j]+cost;
						}
					}
				}
				if ((bzz)&&(!bz[y]))
				{
					bz[y]=1;
					list[++tail]=y;
				}
			}
			bz[x]=0;//就是这里,比赛忘记加少了50分
		}
		tot=0;
		for (i=0;i<=n;i++)
		{
			if (dis[B][i]<dis[0][0])
			{
				while ((tot>1)&&((path[tot-1][1]<=path[tot][1])||(xl(path[tot-1][0],path[tot-1][1],path[tot][0],path[tot][1])>xl(path[tot][0],path[tot][1],i,dis[B][i]))))
				tot--;
				tot++;
				path[tot][0]=i;
				path[tot][1]=dis[B][i];
                //斜率优化,把有用的决策点拎出来
			}
		}
		while ((tot>1)&&((path[tot-1][1]<=path[tot][1])||(xl(path[tot-1][0],path[tot-1][1],path[tot][0],path[tot][1])>xl(path[tot][0],path[tot][1],i,dis[B][i]))))
		tot--;
		if (tot==0)
		{
			printf("0 0
");
			continue;
		}
		if (path[1][0]!=0)
		{
			printf("inf
");
			continue;
		}
		sum1=1;
		sum2=path[1][1];
		lastsum=path[1][1];
		for (i=2;i<=tot;i++)
		{
			rr=lower(-xl(path[i-1][0],path[i-1][1],path[i][0],path[i][1]));
			if (i==tot) ll=1;
			else
			{
				ll=upper(-xl(path[i][0],path[i][1],path[i+1][0],path[i+1][1]));
			}//计算当前决策点作为最优解(最短路)时,x的上界和下界。
			if (js(rr,path[i][0],path[i][1])==lastsum)//去重
			rr--;
			if (rr<ll) continue;
			sum1=sum1+(rr-ll+1);
			sum2=sum2+(js(ll,path[i][0],path[i][1])+js(rr,path[i][0],path[i][1]))*(rr-ll+1)/2;
			lastsum=js(ll,path[i][0],path[i][1]);
		}
		printf("%lld %lld
",sum1,sum2);
	}
}

话说今天AB组放反了

B组难到可怕

B组题目

T1

题目大意

Farmer John决定为他的N头排列好的奶牛(1 <= N<= 200,000)做一张全景合照。这N头奶牛分别以1..N进行编号。他一共拍了M(1<= M <=100,000)张相片,每张相片都只包含有一部分位置连续的奶牛:第i张照片涵盖着编号从a_i到b_i的所有奶牛。当然,这些照片合起来并不保证包含所有的牛。

Farmer John拍摄完所有的相片后,注意到一个很有趣的现象:他拍的每张照片中有且仅有一只奶牛身上有斑点。 FJ知道他的奶牛中有一部分是身上有斑点的,但他从来没有数过这种奶牛的数目。请根据FJ的这些照片,确定可能出现的斑点牛的最大的数目;若从FJ的照片中无法推测斑点牛的数目,则输出-1。

1 <= N<= 200,000;1<= M <=100,000

一眼还以为是线段覆盖之类的东西

谁想得到正解是dp啊

正解

设f[i]表示放了第i个点的,使前面所有的相片都满足条件的最大值。

很显然,f[i]只能由一段区间的f[j]转移来。

对于每张照片我们有两个限制:

  1. 每张照片最多有一个斑点牛
  2. 每个区间至少有一个斑点牛

对于限制1,假如在点i放了一个斑点牛,点i所在的照片就不能有其他斑点牛了,所以设r[i]表示放了i之后,i之前最右边从哪里开始可以再放。r[i]就等于i所在的照片的左端点的最小值-1。

对于限制2,假如在点i放了一头斑点牛,点i左边有一些照片不包括i,要使那些照片都至少有一头斑点牛。那么在不包括i的,在i左侧的线段来说,必须要在左端点之后放一个点。所以所以设l[i]表示放了i之后,i之前最左边可以从哪里开始放。l[i]就等于i左侧的照片的左端点的最大值。

算l[i]与r[i]的时候不用区间修改线段树,有巧妙的方法:

读入线段(x,y)时,用x更新l[y+1],用x-1更新r[y]

然后正着扫一遍,用l[i-1]更新l[i];倒着扫一遍,用r[i+1]更新r[i],r[i]=min(r[i],i-1)

f[i]=max(f[j]+1) | l[i]<=j<=r[i]

由于l[i]和r[i]都是单调递增的,所以可以用单调队列优化一下

答案是f[n+1]-1

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n,m,i,j,x,y;
int f[200005],l[200005],r[200005];
int list[200005],head,tail,bz;

int main()
{
	freopen("read.in","r",stdin);
	scanf("%d%d",&n,&m);
	for (i=1;i<=n+1;i++) r[i]=i-1;
	for (i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		l[y+1]=max(l[y+1],x);
		r[y]=min(r[y],x-1);
	}
	for (i=1;i<=n+1;i++)
	l[i]=max(l[i],l[i-1]);
	r[n+1]=n;
	for (i=n;i>=1;i--)
	r[i]=min(r[i],r[i+1]);
	list[1]=0;
	head=tail=1;
	bz=0;
	for (i=1;i<=n+1;i++)
	{
		while (bz+1<=r[i])
		{
			bz++;
			while ((tail>=head)&&(f[list[tail]]<f[bz])) 
			tail--;
			tail++;
			list[tail]=bz;
		}
		while ((head<=tail)&&(list[head]<l[i])) 
		head++;
		if ((tail>=head)&&(f[list[head]]!=-1))
		{
			f[i]=f[list[head]]+1;
		}
		else
		{
			f[i]=-1;
		}
	}
	if (f[n+1]==-1) printf("-1");
	else printf("%d",f[n+1]-1);
}

T3

题目大意

Farmer John的奶牛最近收到一块大理石。但不幸的是,这块石头有些不完整。为了说明这块石头的状况,我们就可以用一个N*N正方形网格(5 <= N <=300)来描述,其中字符'*'代表石头的缺损部分,'.'表示石头完美无瑕的部分。

奶牛要在这一块大理石上雕刻数字“8”。然而,牛也需要FJ的帮助,以确定在这块大理石上最佳的雕刻位置。这里有几个要求来定义一个有效的数字8:

*数字8由上下两个矩形构成。

*数字8的上下两个矩形都满足至少有一个单元格在矩形内部,也就是说两个矩形都至少是3*3的。

*数字8顶部的矩形的底边必须为底部矩形顶边的子集。

*数字8只能刻在大理石完美无瑕的部分。

*规定数字8的得分为上矩形和下矩形的面积的乘积。

请确定奶牛所能构造的最大数字8.

这题的标签是brute force 然而正解是O((n^3))dp

正解

由于上下矩形的边界是重合一部分的,所以考虑分别求出上下矩形的面积再合并。

对于上矩形,设f[i][j][k]表示左边界为i,右边界为j,下边界为k的上包围结构。

[f[i][j][k]=egin{cases} -1 qquad i或j有缺损\ f[i][j][k-1] qquad f[i][j][k-1]!=-1\ 0 qquad i~j之间的格子均无缺损 end{cases} ]

定义s[i][j]表示以i~j为底边时上矩形的面积最大值。

[s[i][j]=max((f[i][j][k]-2)*(j-i-1))quad ifquad f[i][j][k]>=2 ]

对于下矩形同理。

最后枚举下矩形的上边界,定义S[i][j]为max(s[x][y])(i<=x<=y<=j)即可方便地计算答案

以上都是纸上谈兵

T2

题目大意

Farmer John 正在在计划自己的农场漫步。他的农场的结构就像一棵树:农场有N个谷仓(1<= N <=100,000),分别由N-1条路链接。这样,他便可以通过这些谷仓间的道路遍及各个谷仓。Farmer John想要选择一条路线:这条路线的起点和终点分别为农场中两个不同的谷仓,这条路线不能重复经过一条边两次。Farmer John担心这条路径可能会偏长,所以他想在路线上寻找一个休息点(当然这个休息点不能为起点或者终点)。

每条边的两旁都是牛群,要么是Charcolais(白毛),要么是Angus(黑毛)。Farmer John是一个聪明人,所以他想要在他通过小路的同时平衡小路两侧阴阳的力量。他要选择一条路径使得他从起点到休息站,和从休息站到终点这两段路上都满足路两边的Charcolais牛群和Angus牛群总数量相同。

Farmer John好奇他能找到多少条如上所述的平衡的路径。我们认为,当且仅当两条路线的边的集合不同时,这两条路径才被认为是不同的,否则认为是相同的路线。就算路线上有多个有效的“休息站”的位置能使路线平衡,我们也只记为一条路线。

请帮助计算有多少条不同的平衡路线。

1<= N <=100,000

正解

点分治!!!

鬼知道为什么B组会有这玩意

留个坑以后再搞

原文地址:https://www.cnblogs.com/leason-lyx/p/11172481.html