2021统一省选 A卷 day1解析

题面,就参照洛谷的吧。

  1. 卡牌游戏
  2. 矩阵游戏
  3. 图函数

重点看思维过程。

卡牌游戏

  1. 爆搜的话,过前两个点没问题。枚举要翻牌的数量m,然后从n张中翻m张,直接统计最大最小值。

  2. 考虑到卡牌是按照正面数值由小到大排序的,设c[]数组,将a[]和b[]存入c[],然后由小到大排序。枚举最小值minn为c[i],最大值maxn为c[j],这两个值是不可更改的,现在转为判定性问题,注意minn和maxn不能是同一张牌的,要判别一下。那么正面数值在这个区域的,是不用翻牌的。对于正面数据a[]来说,假设最小值minn的下标为i,最大值maxn的下标为j。如下:
    在这里插入图片描述
    那么区间[i,j]是不需要翻牌的。区间[1,i-1]和[j+1,n]是需要翻牌的。最后看确定的最小值和最大值的下标是否在翻牌区域,如果不在翻牌区域,需要m减1或者减2,有几个不在翻牌区域,就减几个。然后只要满足:翻牌数量小于等于m和翻牌区间的最值在minn和maxn之间,就是合法解。。用ST算法维护区间最大值和区间最小值即可。整体复杂度是O(n^2)的。可以过前4个点。

  3. 以上思路稍加调整就可以过第5~6个点。虽然n值较大,达到了5e5,但是m值最大1000。因此枚举的时候,直接枚举前半段翻牌的长度为x,则后半段翻牌的长度最大为m-x。其余同思路2。整体复杂度是O(m^2)。

  4. 为了加速上述的判定,可以用二分法,就是二分答案。这个题目的答案具有单调性,可以转为判定性问题。假设极值为x,现在假定最小值为minn,那么最大值maxn为minn+x。最大和最小值都得是卡牌上的数值。ai值小于minn的部分需要翻牌,而且得保证翻出的牌的最值在minn和maxn之间,ai值大于maxn的也需要翻牌,而且得保证翻出的牌的最值在minn和maxn之间。否则,这组不满足,跳过。
    如何确定最小值minn呢?需要正面和反面的数值一起,设数组c[],依次存入a[]和b[],然后排序,由小到大,需要记下c[]中的数据在原先数列中的下标。用map就可以。因为2n个数据都不相同,所以不矛盾。在c[]中依次枚举作为最小值。最后看确定的最小值和最大值的下标是否在翻牌区域,如果不在翻牌区域,需要m减1或者减2,有几个不在翻牌区域,就减几个。然后只要满足:翻牌数量小于等于m和翻牌区间的最值在minn和maxn之间,就是合法解。枚举时复杂度O(n),查询最值O(logn),共二分次数O(logn),整体复杂度为O(lognnlogn)。可以过全部点。

矩阵游戏

发现由 b i , j b_{i,j} bi,j倒推出 a i , j a_{i,j} ai,j有很多种方法。比如:对于b[1][1],a[1][1]=a[1][2]=a[2][1]=b[1][1]/4,a[2][2]=b[1][1]/4+b[1][1]%4。其他的位置填的话,只需要再确定两个值就可以了,一个值可以填b[][]/2,另一个值填b[][]/2+b[][]%2。这样倒推出来的是没有限制的a[][]。注意限制是:a[i][j]>=0&&a[i][j]<=1e6。我们发现对于某一列,如果从上到下依次加减相同的值,不影响正确性。比如:+x,-x,+x,-x,……,也可以从减开始,比如:-x,+x,-x,+x,……。对于某一行,也是这样。

下面的问题就是如何由得到的一个随机矩阵,通过某种方法转化为合法矩阵。

对于n,m<=3和m=2的情况,感觉应该可以暴力过,但是想不到好的方法。因为按照上面的思路,如何将随机矩阵转化为合法矩阵,总要用到差分约束,也不是暴力做法。直接考虑全部数据规模。

对于奇数行,在行上的改变从加开始,对于偶数行,从减开始。

对于奇数列,在列上的变化从减开始,对于偶数列,从加开始。

在这里插入图片描述
对于某个位置,要经过行上的和列上的变化。这些变化要同时满足所有位置,得列不等式方程组。比如对于(1,1),需要满足: a [ 1 ] [ 1 ] + x 1 − y 1 > = 0 a[1][1]+x1-y1 >= 0 a[1][1]+x1y1>=0 && a [ 1 ] [ 1 ] + x 1 − y 1 < = 1 e 6 a[1][1]+x1-y1 <= 1e6 a[1][1]+x1y1<=1e6。所有的位置都要满足上述类似的要求。

这就最终能化成类似这样的不等式:a<=x1-x2<=b,这个不等式可以拆成:x1<=x2+b和x2<=x1+(-a)。典型的差分约束系统,用最短路解就可以了。总共是n+m个点,n*m条边。spfa可以过。

图函数

几乎每次大考中都会有这种重新定义的题目,一般比较绕,但是跟着样例模拟一遍,就能大致明白它的套路。

n<=10的前4个点,可以直接模拟过。判断两个点是否连通,可以用dfs搜。链式前向星存图,用vis[]对每条边打标记,删除某条边,就是vis置为0。

以下有点长,但不难理解,耐心阅读。

再具体考虑。对于图G,对于点u,判断点v是否能和点u构成点对,那么点v应该满足什么条件呢?首先要明确的是点u和点u能构成点对,然后消去自身,因此v<u。v->u和u->v之间的点满足什么条件呢?之间的点都是>v的。假设v->u和u->v之间存在点x,而且x<=v。那么x->u和u->x之间也是连通的,这样x及其边要被消去,因此等到枚举到v时,x已经不存在了。假设矛盾。

明白这一点的话,也就没必要真正地删除边了,只需要做一些转换。对于任意的图G,有两点v和u,v<u,如果v->u和u->v之间的点都是>v的,那么u和v构成点对。本题求的分别是原图G的点对数,删除一条边的点对数,删除两条边的点对数,……,删除m条边的点对数。真的要一个图一个图的去求吗?肯定不会。应该能想到,这些m+1个图,会有某种连续性。

假设上述对应的图分别为G0,G1,G2,……,Gm。有两点v和u,v<u。v->u和u->v某条路径上的边的最小值为i,也就是说v->u和u->v的路径至少会到删除第i次边的时候才会被破坏。也就是说点u和v,对于图G0,G1,G2,……,Gi都是点对。v和u之间双联通的路径会只有一条吗?也许不会,所以要求出v和u之间所有路径边的最小值的最大值。因为我们希望u和v能产生更多的贡献,所以要最大值。设数组f[i][j]用来记录合法点对i和j之间的边的编号的最小值的最大值,那么上述u和v对答案的贡献就可以用min(f[u][v],f[v][u])来表示。

设数组g[i]表示第i条边对其前面的图的贡献,前面的图指的是:G0,G1,……,Gi-1。想求第i个图总共有多少点对,只要求出第i条边后面的边对它的贡献即可,也就是后缀和。

现在难点就是如何求这个f[i][j]?可以用floyed。首先要明白:对于floyed,在最外层循环到k时,dis[i][j]最短路径上除了i和j两点,路径上的其他结点编号都小于等于k。我们在学习最小环问题的时候讨论过这个问题。

在这里插入图片描述
以下是最小环问题的算法。

在这里插入图片描述
我们要计算f[i][j],假设i<j,要求是i->j和j->i之间的点都是>j的,如何保证在floyed的过程中f[i][j]的点满足这个条件?只要将松弛点k从大到小枚举就搞定了!这样在做到f[i][j]时,上面的点除了i和j外都是大于j的。

可参考以下代码,1000的规模的floyed开o2,居然能过。

#include<cstdio>
using namespace std;
int n,m,x,y,f[1010][1010],g[200010],ans[200010];
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) 
		scanf("%d%d",&x,&y),f[x][y]=i; 
	for(int k=n;k>=1;k--)
		for(int i=1;i<=n;i++)
			if(f[i][k])
			{
				if (i>k)
				{
					for(int j=1;j<k;j++)
						f[i][j]=max(f[i][j],min(f[i][k],f[k][j]));
				}
				else
				{
					for(int j=1;j<=n;j++) 
						f[i][j]=max(f[i][j],min(f[i][k],f[k][j]));
				}
			}
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			g[min(f[i][j],f[j][i])]++;
	ans[m+1]=n;
	for(int i=m;i>=1;i--)
		ans[i]=ans[i+1]+g[i];
	for(int i=1;i<=m+1;i++)
		printf("%d ",ans[i]);
	return 0;
}

备注:代码转载自洛谷“永恒之眼”的博客。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值