CodeForces - 731D 80-th Level Archeology(线段树+暴力/差分)

题目链接:点击查看

题目大意:给出 n 个数列,再给出一个模数 mod,每次操作可以将所有的数字进行:x = x %mod + 1 操作,问至少进行多少次操作,才能使得 n 个数列按照字典序非降序排列

题目分析:思维不够暴力来凑。。感觉很像是威海线段树维护哈希暴力取模的那个题,事实证明是可以类比过去的

先说我的思路,对于任意两个相邻的数列来说,先找出其首个不相同的位置 pos,然后记录一下其值,在代码中我记做了 a 和 b,因为对于这两个数列来说,a 和 b 的大小关系就直接决定了这两个数列的相对大小关系,所以只需要关注这里即可

然后用线段树维护一下这最多 n - 1 个大小关系,也就是每个节点维护一下每个关系中 b - a 的值,因为我们想要使得 n 个数按照字典序非降序排列,也就是所有的 a <= b ,也就是 b - a >= 0,此时我们就可以暴力枚举更新次数,每次将所有的 a 和 b 都加一,此时考虑三种情况:

  1. 如果 a > b,且 a == mod,那么在一次更新后,a = 1,且 b - a 的值会发生改变
  2. 如果 b > a,且 b == mod,那么在一次更新后,b = 1,且 b - a 的值会发生改变
  3. 否则 b - a 的值不变

不难看出每个关系中的 a 和 b 至多更新一次,因为暴力枚举更新次数的话只需要枚举 [ 0 , mod - 1 ] 就足够了,所以对于每个位置维护一个变量 mmin 用来记录当前节点还剩几次 “加一操作” 后才需要更新即可,这样均摊时间复杂度是 O( nlogn ) 的

然后去网上看了题解,发现果然有简单做法,那就是更需要思维的差分

依然是借助上文中的 a 和 b 继续讲,对于一对相对大小关系 ( a , b ) 来说,仍然是分两种情况讨论一下:

  1. a > b:更新次数 x 在 [ mod - a + 1 , mod - b ] 内是合法的,也就是满足了 ( x + a )%mod + 1 < ( x + b )%mod + 1
  2. b > a:同上,更新次数在 [ 0 , mod - b ] 和 [ mod - a + 1 , mod ] 内是合法的
  3. 没有冲突,比如 123 和 1234,永远是 "123" < "1234":执行任意次操作都符合条件,即 [ 0 , mod ] 内都是可行的

所以我们只需要找到某一个更新次数 i ,使得此时差分数组的前缀和等于 n - 1 就好了

代码:

线段树+暴力

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
     
typedef long long LL;
     
typedef unsigned long long ull;
     
const int inf=0x3f3f3f3f;

const int N=1e6+100;

int n,m;

vector<int>a[N];

vector<pair<int,int>>node;

struct Node
{
	int l,r;
	int mmin;//最小更新时间
	int delta;//b-a
	int a,b;
	int lazy;//最小更新时间的lazy 
}tree[N<<2];

void pushup(int k)
{
	tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);
	tree[k].delta=min(tree[k<<1].delta,tree[k<<1|1].delta);
}

void pushdown(int k)
{
	if(tree[k].lazy)
	{
		int lz=tree[k].lazy;
		tree[k].lazy=0;
		tree[k<<1].lazy+=lz;
		tree[k<<1|1].lazy+=lz;
		tree[k<<1].mmin+=lz;
		tree[k<<1|1].mmin+=lz;
	}
}

void build(int k,int l,int r)
{
	tree[k].l=l;
	tree[k].r=r;
	tree[k].lazy=0;
	if(l==r)
	{
		int a,b;
		tie(a,b)=node[l-1];
		tree[k].mmin=min(m-a+1,m-b+1);
		tree[k].delta=b-a;
		tree[k].a=a;
		tree[k].b=b;
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}

void update()
{
	tree[1].mmin--;
	tree[1].lazy--;
}

void update(int k)
{
	if(tree[k].mmin>0)
		return;
	if(tree[k].l==tree[k].r)
	{
		if(tree[k].a>tree[k].b)
		{
			tree[k].b+=m-tree[k].a+1;
			tree[k].a=1;
			tree[k].delta=tree[k].b-tree[k].a;
			tree[k].mmin=m-tree[k].b+1;
		}
		else
		{
			tree[k].a+=m-tree[k].b+1;
			tree[k].b=1;
			tree[k].delta=tree[k].b-tree[k].a;
			tree[k].mmin=m-tree[k].a+1;
		}
		return;
	}
	pushdown(k);
	update(k<<1);
	update(k<<1|1);
	pushup(k);
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		int num;
		scanf("%d",&num);
		for(int j=1;j<=num;j++)
		{
			int x;
			scanf("%d",&x);
			a[i].push_back(x);
		}
	}
	for(int i=2;i<=n;i++)//查找相邻两个数列中的a和b
	{
		bool flag=(a[i-1].size()>a[i].size());
		for(int j=0;j<min(a[i].size(),a[i-1].size());j++)
		{
			if(a[i][j]==a[i-1][j])
				continue;
			node.push_back(make_pair(a[i-1][j],a[i][j]));
			flag=false;
			break;
		}
		if(flag)//特判1234和123的情况
			return 0*puts("-1");
	}
	if(node.empty())//没有冲突,无需更改
		return 0*puts("0");
	build(1,1,node.size());
	for(int i=0;i<=m;i++)
	{
		if(tree[1].delta>0)
			return 0*printf("%d\n",i);
		update();
		update(1);
	}
	puts("-1");













    return 0;
}

差分:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
     
typedef long long LL;
     
typedef unsigned long long ull;
     
const int inf=0x3f3f3f3f;

const int N=1e6+100;

vector<int>a[N];

int delta[N];

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		int num;
		scanf("%d",&num);
		for(int j=1;j<=num;j++)
		{
			int x;
			scanf("%d",&x);
			a[i].push_back(x);
		}
	}
	for(int i=2;i<=n;i++)//查找相邻两个数列中的a和b
	{
		bool flag=true;
		for(int j=0;j<min(a[i].size(),a[i-1].size());j++)
		{
			if(a[i][j]==a[i-1][j])
				continue;
			int x=a[i-1][j],y=a[i][j];
			if(x>y)
			{
				delta[m-x+1]++;
				delta[m-y+1]--;
			}
			else
			{
				delta[0]++;
				delta[m-y+1]--;
				delta[m-x+1]++;
				delta[m]--;
			}
			flag=false;
			break;
		}
		if(flag)
		{
			if(a[i-1].size()>a[i].size())//特判1234和123的情况
				return 0*puts("-1");
			else//特判123和1234的情况
			{
				delta[0]++;
				delta[m]--;
			}
		}
	}
	int sum=0;
	for(int i=0;i<m;i++)
	{
		sum+=delta[i];
		if(sum==n-1)
			return 0*printf("%d\n",i);
	}
	puts("-1");








    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Frozen_Guardian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值