题目链接:点击查看
题目大意:给出 n 个数列,再给出一个模数 mod,每次操作可以将所有的数字进行:x = x %mod + 1 操作,问至少进行多少次操作,才能使得 n 个数列按照字典序非降序排列
题目分析:思维不够暴力来凑。。感觉很像是威海线段树维护哈希暴力取模的那个题,事实证明是可以类比过去的
先说我的思路,对于任意两个相邻的数列来说,先找出其首个不相同的位置 pos,然后记录一下其值,在代码中我记做了 a 和 b,因为对于这两个数列来说,a 和 b 的大小关系就直接决定了这两个数列的相对大小关系,所以只需要关注这里即可
然后用线段树维护一下这最多 n - 1 个大小关系,也就是每个节点维护一下每个关系中 b - a 的值,因为我们想要使得 n 个数按照字典序非降序排列,也就是所有的 a <= b ,也就是 b - a >= 0,此时我们就可以暴力枚举更新次数,每次将所有的 a 和 b 都加一,此时考虑三种情况:
- 如果 a > b,且 a == mod,那么在一次更新后,a = 1,且 b - a 的值会发生改变
- 如果 b > a,且 b == mod,那么在一次更新后,b = 1,且 b - a 的值会发生改变
- 否则 b - a 的值不变
不难看出每个关系中的 a 和 b 至多更新一次,因为暴力枚举更新次数的话只需要枚举 [ 0 , mod - 1 ] 就足够了,所以对于每个位置维护一个变量 mmin 用来记录当前节点还剩几次 “加一操作” 后才需要更新即可,这样均摊时间复杂度是 O( nlogn ) 的
然后去网上看了题解,发现果然有简单做法,那就是更需要思维的差分
依然是借助上文中的 a 和 b 继续讲,对于一对相对大小关系 ( a , b ) 来说,仍然是分两种情况讨论一下:
- a > b:更新次数 x 在 [ mod - a + 1 , mod - b ] 内是合法的,也就是满足了 ( x + a )%mod + 1 < ( x + b )%mod + 1
- b > a:同上,更新次数在 [ 0 , mod - b ] 和 [ mod - a + 1 , mod ] 内是合法的
- 没有冲突,比如 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;
}