对于所有方案,必须满足的是 个人的位置是否合法。
而 又很小,考虑去搜索安排。
我们去 这 个人。
假设第 个已经安排座位的人的编号为 ,需要坐到 ,那么就需要坐在 ~ 之间的人都在 之前选择位置。
我们根据这个来搜索,顺便剪枝。
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define db double
#define sg string
#define ll long long
#define pb push_back
#define pll pair<ll,ll>
#define make make_pair
#define rel(i,x,y) for(ll i=(x);i<(y);i++)
#define rep(i,x,y) for(ll i=(x);i<=(y);i++)
#define red(i,x,y) for(ll i=(x);i>=(y);i--)
#define res(i,x) for(ll i=head[x];i;i=nxt[i])
using namespace std;
const ll N=1e2+5;
const ll Inf=1e18;
ll n,m,mod,ans,s[N],fac[N],C[N][N];
struct node {
ll p,q;
bool operator < (const node &a) const {
return p<a.p;
}
}seat[30];
inline ll read() {
ll x=0;char ch=getchar();bool f=0;
while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?-x:x;
}
inline ll add(ll x,ll y) {
return x+y>=mod?x+y-mod:x+y;
}
inline ll mul(ll x,ll y) {
return x*y%mod;
}
int check(int id) {
ll ret=s[id]-seat[id].q;
vector<pll>v;
rel(i,0,id) {
if(s[id]>=seat[i].q&&s[id]<=s[i]) return -1;
if(s[i]>=seat[id].q&&s[i]<s[id]) v.pb(make(max(seat[id].q,seat[i].q),s[i]));
}
sort(v.begin(),v.end());
rel(i,1,v.size()) {
if(v[i].first-1<=v[i-1].second) {
v[i].first=v[i-1].first;
v[i].second=max(v[i].second,v[i-1].second);v[i-1]=make(0,-1);
}
}
rel(i,0,v.size()) ret-=v[i].second-v[i].first+1;
return ret;
}
void dfs(ll id,ll sum,ll last) {
if(id==m) {
ans=add(ans,mul(sum,fac[last]));return ;
}
rep(i,seat[id].q,min(n,seat[id].q+seat[id].p-1)) {
s[id]=i;
ll ret=check(id);
if(ret>-1) dfs(id+1,mul(sum,mul(C[last][ret],fac[ret])),last-ret+seat[id+1].p-seat[id].p-1);
}
}
int main() {
n=read(),m=read(),mod=read();
rel(i,0,N) C[i][0]=1%mod;
rel(i,1,N) rep(j,1,i) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
fac[0] = 1;
rep(i,1,n) fac[i]=mul(fac[i-1],i);
rel(i,0,m) seat[i].p=read(),seat[i].q=read();
sort(seat,seat+m);
seat[m].p=n+1;
dfs(0,1,seat[0].p-1);
printf("%lld\n",ans);
return 0;
}