题目大意
有大小为
n
(
1
≤
n
≤
1
0
3
)
n(1 \leq n \leq 10^3)
n(1≤n≤103) 的未知排列
p
p
p 和
m
(
1
≤
m
≤
1
0
3
)
m(1 \leq m \leq 10^3)
m(1≤m≤103) 个不同的区间,第
i
i
i 个区间是
[
l
i
,
r
i
]
[l_i , r_i]
[li,ri] 。保证对于任意两个区间
i
,
j
i,j
i,j ,若
l
i
≤
l
j
l_i \leq l_j
li≤lj ,则
r
i
≤
r
j
r_i \leq r_j
ri≤rj 或
r
i
>
r
j
r_i > r_j
ri>rj。
有
m
m
m 条限制,第
i
i
i 条限制是区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] 内的逆序数对必须是奇数/偶数个。
求满足条件的排列
p
p
p ,若不存在输出
−
1
-1
−1 。
题解
本题要求的逆序数对个数奇偶性。
考虑一个子区间中:
1.原排列逆序数对数为
0
0
0;
2.再随机交换两个数的位置则其逆序数对数为
1
1
1。
由题意可知,每两个区间的关系是“包含”或“并列”。
如果想要表示其从属关系,那么用树再好不过了。
并列
容易看出,当两个区间并列时,其逆序数对个数互不影响。
包容
一个大区间中逆序数对数为所有子区间个数之和。
我们可以通过DFS顺序建树来统计子节点中的逆序数对总数。
如果个数奇偶性不同,则只要再加一个逆序数对即可。
考虑如何增加一个逆序数对:
新增
n
n
n 个区间
(
i
,
i
,
0
)
(i,i,0)
(i,i,0),
若不为叶节点,则其中必有两个及以上子节点,
我们灵机一动,想到交换第一个区间的最大值和第二个区间的最小值,
则既不会影响相对位置,又能改变结果。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e3+5;
vector<int> son[N];
struct node{
int l,r,w,l1,r1;
}f[N];
int n,m,root,t;
int vis[N],pos[N];
int fa[N];
int g[N];
int q[2000][2000][3];
int cmp(node a,node b)
{
if(a.l==b.l)
return a.r>b.r;
return a.l<b.l;
}
void dfs(int x)
{
vis[x]=1;
int sum=0,ans=0;
for(int j=x+1;j<=n+t&&f[j].r<=f[x].r;j++) //在左端点排序的前提下,原顺序就是dfs序列
{ //观察一下右端点位置即可得出相对关系
if(!vis[j])
{
son[x].push_back(j);
sum++;
if(j+1<=n+t && f[j].r>=f[j+1].r)
dfs(j);
vis[j]=1;
ans^=f[j].w;
}
}
if(ans!=f[x].w)
{
int maxx=0,id=0;
for(int i=f[son[x][0]].l;i<=f[son[x][0]].r;i++) //暴力求解最大值和最小值
if(pos[i]>maxx)
maxx=pos[i],id=i;
int minn=n+1,it=0;
for(int i=f[son[x][1]].l;i<=f[son[x][1]].r;i++)
if(pos[i]<minn)
minn=pos[i],it=i;
swap(pos[id],pos[it]);
}
}
int main()
{
scanf("%d%d",&n,&m);
int b=0;
t=0;
for(int i=1;i<=m;i++)
{
t++;
scanf("%d%d%d",&f[t].l,&f[t].r,&f[t].w);
f[t].l1=f[t].l;
f[t].r1=f[t].r;
if(f[t].r-f[t].l==0)
{
if(f[t].w==1)
b=1;
t--;
}
}
if(b)
{
puts("-1");
return 0;
}
for(int i=1;i<=n;i++)
f[++t]={i,i,0,i,i};
sort(f+1,f+t+1,cmp);
for(int i=1;i<=n;i++)
pos[i]=i;
for(int i=1;i<=n+t;i++)
{
if(!vis[i])
dfs(i);
}
for(int i=1;i<=n;i++)
printf("%d ",pos[i]);
}
总结
1
0
3
10^3
103 的数据给了它任性的理由,
只要能想到建树和区间的特点,这题就能解决了。