GDOI2008 指纹 题解

题意

n n n张图片,第 i i i张图片有 A , B , C , D A,B,C,D A,B,C,D四个元素。

称一张图片是累赘的:有两张图片 x , y x,y x,y,如果 x x x有至少三个元素大于 y y y所对应的三个元素,那个 y y y就是累赘的。

例如 x ( A , B , C , D ) = ( 2 , 3 , 3 , 4 ) ,   y ( A , B , C , D ) = ( 4 , 2 , 5 , 6 ) x(A,B,C,D)=(2,3,3,4),\ y(A,B,C,D)=(4,2,5,6) x(A,B,C,D)=(2,3,3,4), y(A,B,C,D)=(4,2,5,6),其中 x A > y A ,   x C > y C ,   x D > y D x_A>y_A,\ x_C>y_C,\ x_D>y_D xA>yA, xC>yC, xD>yD,那么 y y y就是累赘的。要注意, x x x有可能是累赘的。

输出有多少张累赘的图片 m m m张,接下来 m m m行由小到大输出累赘图片的编号。

1 ≤ n ≤ 1 0 5 1 \le n\le 10^5 1n105

输入

6
1 1 2 6
2 3 3 4
3 4 1 3
4 2 6 5
5 6 5 1
6 5 4 2

输出

4
2
4
5
6

思路

(看着像偏序)

先把每 i i i张图片的四个信息,转化成 4 4 4个信息下 i i i个元素各自的排名。

因为称 y y y图片是累赘的,只需要找到一张图片 x x x存在三个元素大于 y y y所对应的就行,并把 y y y标记为 u l s uls uls的(useless 累赘)。至于是哪一张图片使得 y y y成为累赘的并不重要,这是最关键的。

那么可以四个里面随便钦定三个元素进行比对,那么只需 4 4 4次比对。设为 a , b , c a,b,c a,b,c为比对的元素,考虑“以累赘的视角计算自己怎么被搞成累赘的”。

先按 a a a元素排序,然后在树状数组上,以 b b b元素的排名作为下标,维护一个最小 c c c元素排名值。

听着很抽象啊!

假设当按 a a a元素从小到大排序,所对应的 p o s pos pos为原下标,对应的 b , c b,c b,c属性排名分别是是 b p o s , c p o s b_{pos},c_{pos} bpos,cpos,每次向下标 b p o s b_{pos} bpos加入 c p o s c_{pos} cpos取最小值。

在树状数组上查询 b b b属性排名在 [ 1 , b p o s ) [1,b_{pos}) [1,bpos)的最小 c c c属性的排名记为 m i n c minc minc,此时假设 ∃ p o s ′ ,   m i n c = c p o s ′ \exist pos',\ minc=c_{pos'} pos, minc=cpos

显然在树状数组上查找,显然 b p o s ′ ∈ [ 1 , b p o s ) , b p o s ′ < b p o s b_{pos'}\in [1,b_{pos}),b_{pos'}bpos[1,bpos)bpos<bpos;因为下标 p o s ′ pos' pos肯定在 p o s pos pos前被处理,所以 a p o s ′ < a p o s a_{pos'}apos<apos——现在 p o s ′ pos' pos已经有两个元素小于 p o s pos pos了。

如果 m i n c = c p o s ′ < c p o s minc=c_{pos'}minc=cpos<cpos,那么就满足 p o s ′ pos' pos有三个元素小于 p o s pos pos了, p o s pos pos可以被标记为 u l s uls uls。又因为 p o s ′ pos' pos能使 p o s pos pos成为累赘,必然也能使“能被 p o s pos pos搞成累赘的”标记为累赘,所以不必把 p o s pos pos c p o s c_pos cpos放进树状数组了。

否则 p o s pos pos就不是累赘的,且 c p o s ≤ c p o s ′ c_{pos}\le c_{pos'} cposcpos b p o s b_{pos} bpos下的 c p o s c_{pos} cpos有可能能比“接下来处理的, a a a元素排名肯定比当前 a p o s a_{pos} apos大的, b b b元素排名比 b p o s b_{pos} bpos大的图片 x x x"的 c c c元素要小,使 x x x成为累赘,因此把 p o s pos pos c p o s c_{pos} cpos放进树状数组里。

好累啊~~

时间复杂度 Θ ( n log ⁡ 2 n ) \Theta(n \log_2 n) Θ(nlog2n)

#include
using namespace std;
#define ll long long
const ll N=2e6+9,inf=0x3f3f3f3f;
ll n,m,a[5][N];
bool uls[N];//useless
struct BT
{
	ll T[N];
	ll lowbit(ll x)
	{
		return x&(-x);
	}
	void add(ll x,ll k)
	{
		for(int i=x;i<N;i+=lowbit(i))
		T[i]=min(T[i],k);
	}
	ll query(ll x)
	{
		ll ret=inf;
		for(int i=x;i>=1;i-=lowbit(i))
		ret=min(ret,T[i]);
		return ret;
	}
}B;
struct node
{
	ll rk,id;
}p[N];
bool cmp(node x,node y)
{
	return x.rk<y.rk;
}
void sol(ll *a,ll *b,ll *c)
{
	memset(B.T,inf,sizeof(B.T));
	for(int i=1;i<=n;i++)
	p[i]=(node){a[i],i};
	sort(p+1,p+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		ll pos=p[i].id,minc=B.query(b[pos]);
		if(c[pos]>minc)uls[pos]=1;
		else B.add(b[pos],c[pos]);
	}
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=4;j++)
	scanf("%lld",&a[j][i]);
	sol(a[1],a[2],a[3]);
	sol(a[1],a[2],a[4]);
	sol(a[1],a[3],a[4]);
	sol(a[2],a[3],a[4]);
	for(int i=1;i<=n;i++)
	m+=uls[i];
	printf("%lld\n",m);
	for(int i=1;i<=n;i++)
	if(uls[i])printf("%lld\n",i);
	return 0;
}

你可能感兴趣的:(算法)