题意
给你一个对a数组hash之后的hash表,求原来的a数组字典序最小的解。
题解
对于hash表中当前这个数hi,若hi%n!=i的话,说明当前这个数是后移过的,那么说明在hi后移的这一段数必须在hi之前放入才能让hi在i这个位置,所以这就是一个拓扑排序。假如我们暴力建图,那么肯定会TLE。所以我们用线段树优化建图,对于当前这个点,他位移的那段区间,找到在线段树上的映射,将这些子区间的点连接到当前节点,然后将当前节点连向包含这个节点的所有线段树上的区间。对于这张图跑拓扑排序,因此总点数为n*4,边数为O(n*log(n)),时间复杂度为O(n+n*log(n))。
AC代码
#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
#define N 200005
using namespace std;
struct node
{
int num;
node(){}
node(int num)
{
this->num=num;
}
};
priority_queue<node>que;
vector<int>vt[N*4],ans;
int a[N],n;
int treeid[N*4],pos[N*4],du[N*4],s[N*4];
bool operator<(node A,node B)
{
return a[treeid[A.num]]>a[treeid[B.num]];
}
void build(int L,int R,int root)
{
vt[root].clear();
treeid[root]=n+1;
du[root]=0;
if(L==R)
{
treeid[root]=L;
pos[L]=root;
return ;
}
int mid=L+R>>1;
build(L,mid,root<<1);
build(mid+1,R,root<<1|1);
}
void update(int L,int R,int l,int r,int root,int who)
{
if(l<=L&&R<=r)
{
du[who]++;
vt[root].push_back(who);
return ;
}
int mid=L+R>>1;
if(l<=mid)update(L,mid,l,r,root<<1,who);
if(r>mid)update(mid+1,R,l,r,root<<1|1,who);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ans.clear();
int sum=0,flag=0;
scanf("%d",&n);
a[n+1]=-1;
build(0,n-1,1);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
s[i+1]=s[i]+(a[i]!=-1);
}
for(int i=0;i<n;i++)
{
if(a[i]==-1)continue;
sum++;
if(a[i]%n!=i)
{
if(a[i]%n<i)
{
if(s[i]-s[a[i]%n]!=i-a[i]%n)flag=1;
update(0,n-1,a[i]%n,i-1,1,pos[i]);
}
else
{
if(s[n]-s[a[i]%n]!=n-a[i]%n)flag=1;
update(0,n-1,a[i]%n,n-1,1,pos[i]);
if(i)
{
if(s[i]!=i)flag=1;
update(0,n-1,0,i-1,1,pos[i]);
}
}
}
int now=pos[i]/2;
while(now)
{
du[now]++;
vt[pos[i]].push_back(now);
now/=2;
}
}
if(flag)
{
printf("-1\n");
continue;
}
for(int i=0;i<n;i++)
if(a[i]!=-1&&du[pos[i]]==0)
que.push(node(pos[i]));
while(!que.empty())
{
node k=que.top();
que.pop();
if(a[treeid[k.num]]!=-1)ans.push_back(a[treeid[k.num]]);
for(int i=0;i<vt[k.num].size();i++)
{
int to=vt[k.num][i];
du[to]--;
if(du[to]==0)que.push(node(to));
}
}
if(sum!=ans.size())printf("-1\n");
else
{
for(int i=0;i<sum-1;i++)
printf("%d ",ans[i]);
if(sum!=0)printf("%d",ans[sum-1]);
printf("\n");
}
}
}