D. Number Of Permutations(容斥定理)

题目描述

You are given a sequence of nn pairs of integers: (a_1, b_1), (a_2, b_2), \dots , (a_n, b_n)(a1​,b1​),(a2​,b2​),…,(an​,bn​) . This sequence is called bad if it is sorted in non-descending order by first elements or if it is sorted in non-descending order by second elements. Otherwise the sequence is good. There are examples of good and bad sequences:

  • s = [(1, 2), (3, 2), (3, 1)]s=[(1,2),(3,2),(3,1)] is bad because the sequence of first elements is sorted: [1, 3, 3][1,3,3] ;
  • s = [(1, 2), (3, 2), (1, 2)]s=[(1,2),(3,2),(1,2)] is bad because the sequence of second elements is sorted: [2, 2, 2][2,2,2] ;
  • s = [(1, 1), (2, 2), (3, 3)]s=[(1,1),(2,2),(3,3)] is bad because both sequences (the sequence of first elements and the sequence of second elements) are sorted;
  • s = [(1, 3), (3, 3), (2, 2)]s=[(1,3),(3,3),(2,2)] is good because neither the sequence of first elements ([1, 3, 2])([1,3,2]) nor the sequence of second elements ([3, 3, 2])([3,3,2]) is sorted.

Calculate the number of permutations of size nn such that after applying this permutation to the sequence ss it turns into a good sequence.

A permutation pp of size nn is a sequence p_1, p_2, \dots , p_np1​,p2​,…,pn​ consisting of nn distinct integers from 11 to nn ( 1 \le p_i \le n1≤pi​≤n ). If you apply permutation p_1, p_2, \dots , p_np1​,p2​,…,pn​ to the sequence s_1, s_2, \dots , s_ns1​,s2​,…,sn​ you get the sequence s_{p_1}, s_{p_2}, \dots , s_{p_n}sp1​​,sp2​​,…,spn​​ . For example, if s = [(1, 2), (1, 3), (2, 3)]s=[(1,2),(1,3),(2,3)] and p = [2, 3, 1]p=[2,3,1] then ss turns into [(1, 3), (2, 3), (1, 2)][(1,3),(2,3),(1,2)] .

输入格式

The first line contains one integer nn ( 1 \le n \le 3 \cdot 10^51≤n≤3⋅105 ).

The next nn lines contains description of sequence ss . The ii -th line contains two integers a_iai​ and b_ibi​ ( 1 \le a_i, b_i \le n1≤ai​,bi​≤n ) — the first and second elements of ii -th pair in the sequence.

The sequence ss may contain equal elements.

输出格式

Print the number of permutations of size nn such that after applying this permutation to the sequence ss it turns into a good sequence. Print the answer modulo 998244353998244353 (a prime number).

题意翻译

题目描述

给出一个由nn个整数二元组组成的序列:(a_1, b_1), (a_2, b_2), \dots , (a_n, b_n)(a1​,b1​),(a2​,b2​),…,(an​,bn​)。我们称这个序列是坏的,当且仅当这个序列中每个二元组的第一个元素是所组成的数列是单调不减或这个序列中每个二元组的第二个元素是所组成的数列是单调不减的。否则,我们称这个序列是好的。下面给出了几个好序列和坏序列的例子:

s = [(1, 2), (3, 2), (3, 1)]s=[(1,2),(3,2),(3,1)] 是一个坏序列,因为序列中每个二元组的第一个元素所组成的数列是排好序的:[1, 3, 3][1,3,3];

s = [(1, 2), (3, 2), (1, 2)]s=[(1,2),(3,2),(1,2)] 是一个坏序列,因为序列中每个二元组的第二个元素所组成的数列是排好序的:[2, 2, 2][2,2,2];

s = [(1, 1), (2, 2), (3, 3)]s=[(1,1),(2,2),(3,3)] 是一个坏序列,因为序列中每个二元组的第一个元素所组成的数列和第二个元素所组成的数列都是排好序的;

s = [(1, 3), (3, 3), (2, 2)]s=[(1,3),(3,3),(2,2)] 是一个好序列,因为序列中每个二元组的第一个元素所组成的数列和第二个元素所组成的数列都是无序的(译注:“单调不减”(non-descending)可能比“无序(not sorted)”更合适)。

计算出大小为nn的排列,使得经过排列操作后,二元组序列ss能变成一个好序列

一个大小为nn的排列方式pp:p_1, p_2, \dots , p_np1​,p2​,…,pn​,包含nn个不重复的正整数( 1 \le p_i \le n)(1≤pi​≤n). 如果排列为p_1, p_2, \dots , p_np1​,p2​,…,pn​,在经过排列操作之后,序列会变为s_{p_1}, s_{p_2}, \dots , s_{p_n}sp1​​,sp2​​,…,spn​​。例如,如果序列s = [(1, 2), (1, 3), (2, 3)]s=[(1,2),(1,3),(2,3)],排列p = [2, 3, 1]p=[2,3,1],那么序列变为[(1, 3), (2, 3), (1, 2)][(1,3),(2,3),(1,2)]。

输入格式

第一行包含一个正整数 nn (1 \le n \le 3 \cdot 10^51≤n≤3⋅105).

接下来的nn行描述了序列ss。第ii行包含两个整数a_iai​和b_ibi​(1 \le a_i, b_i \le n1≤ai​,bi​≤n),表示序列ss的第ii个二元组的两个整数。

序列ss中可能包含相同的二元组。

输出格式

输出符合题意的大小为nn的排列的方案数,使得经过这些排列中的任何一个的排列操作之后,序列ss会成为一个好序列。答案对9982435399824353取模(一个质数)。

输入输出样例

输入 #1复制

3
1 1
2 2
3 1

输出 #1复制

3

输入 #2复制

4
2 3
2 2
2 1
2 4

输出 #2复制

0

输入 #3复制

3
1 1
1 1
2 3

输出 #3复制

4

说明/提示

In first test case there are six permutations of size 33 :

  1. if p = [1, 2, 3]p=[1,2,3] , then s = [(1, 1), (2, 2), (3, 1)]s=[(1,1),(2,2),(3,1)] — bad sequence (sorted by first elements);
  2. if p = [1, 3, 2]p=[1,3,2] , then s = [(1, 1), (3, 1), (2, 2)]s=[(1,1),(3,1),(2,2)] — bad sequence (sorted by second elements);
  3. if p = [2, 1, 3]p=[2,1,3] , then s = [(2, 2), (1, 1), (3, 1)]s=[(2,2),(1,1),(3,1)] — good sequence;
  4. if p = [2, 3, 1]p=[2,3,1] , then s = [(2, 2), (3, 1), (1, 1)]s=[(2,2),(3,1),(1,1)] — good sequence;
  5. if p = [3, 1, 2]p=[3,1,2] , then s = [(3, 1), (1, 1), (2, 2)]s=[(3,1),(1,1),(2,2)] — bad sequence (sorted by second elements);

思路:参考博客:https://blog.csdn.net/tomjobs/article/details/100045809

  • 要考虑多少个不递增的排列是很困难的,正难则反,我们考虑多少个递增的排列,再用全排列减去这个就可以了。
    1.先考虑问题的简化版本:如果是一元组x,并且没有相同的情况,怎么求?此时x的递增情况只有一种,其他情况全是非递增的,只需要n的阶乘-1就可以了。
    2.再考虑:如果一元组x,有重复的数字,怎么办?此时x的递增情况,与其中的重复数组有关,比如1 2 2。第二个2和第三个2可以调换位置。此时的递增数目为所有重复数目阶乘和积。再用fac[n]减去重复部分就可以了。
    3.继续考虑:如果变成了二元组(x,y),没有重复数字:只考虑x的递增情况,有一种,只考虑y的递增情况,有一种。但是!这是二元组,你算x的时候,忽略了y,算y的时候,忽略了x。其中会有重复的部分。假设(x,y)x,y同时递增。那么x递增的情况和y递增的情况是等价的,要减去1.
    4.最后是题目的情况:二元组(x,y),重复数字。只考虑x,y,就是第二种情况的和:重复数目阶乘的积。在考虑第三种情况,重复部分。假设x,y能同时递增,并且出现了相同的二元组,那么这个相同的二元组被计算了两遍,就必须减去相同二元组的情况。

比如(1,2) (2,3) (2,3)(4,5)。
只考虑x是1,2,2,4。有两个2答案是2。
再考虑y是2,3,3,5。有两个3答案是3.
但是你算x的2和你算y的3其实是一种情况——交换2的时候也在交换3,所以要减去二元组的重复部分。


启发:

组合数学的不递增/不递减可以通过整体减去反面来简化思考。

出现二元组可以先降维从一元组考虑,再加慢慢加条件类比到二元组。

二元组的时候可能要通过一元组进行计算,然后在考虑哪些情况用容斥定理去解决

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+100;
typedef long long LL;
const LL mod=998244353;
LL fac[maxn],n;
map<LL,LL>vis1,vis2;
map<pair<LL,LL>,LL>map1;//自定义node放进去的要写排序 
pair<LL,LL>N[maxn];
struct node
{
	LL x,y;
}nodes[maxn];
bool cmp(node A,node B)
{
	if(A.x==B.x) return A.y<B.y;
	return A.x<B.x; 
}
void init()
{
	fac[0]=1;
	for(LL i=1;i<=maxn;i++) fac[i]=fac[i-1]*i%mod;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  init();
  cin>>n;
  for(LL i=1;i<=n;i++){
  	cin>>nodes[i].x>>nodes[i].y;
	vis1[nodes[i].x]++;vis2[nodes[i].y]++;
  }
  for(LL i=1;i<=n;i++)
  {
  	if(vis1[nodes[i].x]==n||vis2[nodes[i].y]==n)
  	{
  		cout<<0<<endl;return 0;
	}
  }
  LL ans=0;
  LL tmp1=1;
  //x递增数目 
  for(LL i=1;i<=n;i++)
  {
  	 if(vis1[i])
  	 tmp1=(tmp1%mod*fac[vis1[i]]%mod)%mod;
  //		tmp1=(tmp1%mod*fac[vis1[nodes[i].x]]%mod)%mod;(重复访问同一个元素了 
  }
  ans=(ans%mod+tmp1%mod)%mod;
  //y递增数目 
  LL tmp2=1;
  for(LL i=1;i<=n;i++)
  {
  	 if(vis2[i])
  	 tmp2=(tmp2%mod*fac[vis2[i]]%mod)%mod;
  //	 tmp2=(tmp2%mod*fac[vis2[nodes[i].y]]%mod)%mod;(重复访问同一个元素了 
  }
  ans=(ans%mod+tmp2%mod)%mod;
  //统计相同的二元组
  sort(nodes+1,nodes+1+n,cmp);//按照x从小到大排序 
  bool flag=1;
  //是否有x和y同时递增 的情况 
  for(LL i=2;i<=n;i++)
  {
  	 if(nodes[i].y<nodes[i-1].y)
  	 {
  	 	flag=0;break;	
	 }
  } 
  if(flag)
  {
  	 LL res=1;
  	 for(LL i=1;i<=n;i++){
  	 	N[i].first=nodes[i].x;
		N[i].second=nodes[i].y;  	
	 }
  	 for(LL i=1;i<=n;i++)
  	 {
  	 	map1[N[i]]++;	
	 }
	 for(map<pair<LL,LL>,LL>::iterator it=map1.begin();it!=map1.end();it++)
	 {
	 	res=(res%mod*fac[it->second]%mod)%mod;
	 }
	 cout<<((fac[n]-ans+res)%mod+mod)%mod<<endl;//减法出现减后+mod 
  }
  else cout<<( ( (fac[n]-ans)%mod+mod)%mod)<<endl;
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/108402516