codeforces1027D Number Of Permutations

传送门:http://codeforces.com/problemset/problem/1207/D

反向求good array,那就在总方案数 n! 中把bad array全部减掉

我们先把数组按照a排序,那么因为第一维是不降的方案就可以计算出来了,就是对于相同的一段a,他们内部可以随便交换位置,那么这段长度为len的相同的a就可以贡献 len! ,把所有len!乘起来就是 第一维不降的方案tmp1

同理按照b排序,求出第二维不降的总方案数tmp2。

现在要算出那些重复的方案tmp3,那就是第一维不降第二维也不降的方案。

那么们就在按a排序的基础上,在一段相同a的内部按b排序,然后我们看整个序列是否满足b不降,如果满足,则去每一段相同的a中 找 b 相同连续段,然后通过上面一样的方式乘出tmp3。如果不满足,那么说明没有重复方案,tmp3=0

最后答案就是 n!-tmp1-tmp2+tmp3

#include<bits/stdc++.h>
#define maxl 400010
using namespace std;

const int mod=998244353;

int n,m,bcnt,ccnt,tcnt;
long long ans;
struct node
{
	int a,b;
}a[maxl],b[maxl],c[maxl];
int bpos[maxl],blen[maxl];
int cpos[maxl],clen[maxl];
int tlen[maxl];
long long jc[maxl];
char s[maxl];

inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].a,&a[i].b);
		b[i]=a[i];c[i]=a[i];
	}
}

inline bool cmpa(const node &x,const node &y)
{
	return x.a<y.a;
}

inline bool cmpb(const node &x,const node &y)
{
	return x.b<y.b;
}

inline void mainwork()
{
	sort(b+1,b+1+n,cmpa);
	sort(c+1,c+1+n,cmpb);
	long long tmp1=1,tmp2=1,tmp3=1;
	bcnt=0;
	for(int i=1;i<=n;i++)
	if(b[i].a==b[i-1].a)
		bpos[i]=bcnt,blen[bcnt]++;
	else
	{
		tmp1=tmp1*jc[blen[bcnt]]%mod;
		bcnt++;
		bpos[i]=bcnt;blen[bcnt]=1;
	}
	tmp1=tmp1*jc[blen[bcnt]]%mod;
	ccnt=0;
	for(int i=1;i<=n;i++)
	if(c[i].b==c[i-1].b)
		cpos[i]=ccnt,clen[ccnt]++;
	else
	{
		tmp2=tmp2*jc[clen[ccnt]]%mod;
		ccnt++;
		cpos[i]=ccnt;clen[ccnt]=1;
	}
	tmp2=tmp2*jc[clen[ccnt]]%mod;
	for(int i=1;i<=n;i++)
	{
		sort(b+i,b+i+blen[bpos[i]],cmpb);
		i=i+blen[bpos[i]]-1;
	}
	bool flag=true;
	for(int i=2;i<=n;i++)
	if(b[i].b<b[i-1].b)
		flag=false;
	if(flag)
	{
		for(int i=1;i<=n;i++)
		{
			tcnt=0;
			for(int j=i;j<=i+blen[bpos[i]]-1;j++)
			if(b[j].a==b[j-1].a && b[j].b==b[j-1].b)
				tlen[tcnt]++;
			else
			{
				tmp3=tmp3*jc[tlen[tcnt]]%mod;
				tcnt++;
				tlen[tcnt]=1;
			}
			tmp3=tmp3*jc[tlen[tcnt]]%mod;
			i=i+blen[bpos[i]]-1;
		}
	}
	else
		tmp3=0;
	ans=((jc[n]-tmp1)%mod+mod)%mod;
	ans=((ans-tmp2)%mod+mod)%mod;
	ans=(ans+tmp3)%mod;
}

inline void print()
{
	printf("%lld\n",ans);
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	jc[0]=1;
	for(int i=1;i<maxl;i++)
		jc[i]=jc[i-1]*i%mod;
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值