Problem:
F: 赤黒そーるじぇむ / Red-Black Soul Gem

問題

ほむちゃんは、そーるじぇむを赤色や黒色に変化させたり、 異なる 2 つのそーるじぇむ間を魔法の糸で接続したりする不思議な力を持っています。
この力を使うことで、ほむちゃんは、そーるじぇむによる魔方陣を作ることができます。

ほむちゃんは、 1 から N で番号付けられた N 個のそーるじぇむがあるとき、以下の条件が成り立つ異なる魔方陣がいくつあるのか気になりました。

どの 2 つのそーるじぇむ間も、高々 1 回しか接続されない。

すべてのそーるじぇむの色が、赤色か黒色のいずれかになっている。

任意のそーるじぇむに対して、自身の色と異なる色のそーるじぇむのうち、少なくとも 1 つと接続されている。

このとき、魔方陣はそーるじぇむを頂点とし、魔法の糸を辺とするグラフとみなすことができます。
ここで、そのグラフは 連結でなくてもよい ことに注意してください。

ほむちゃんは計算が苦手なので、代わりにあなたが高速なプログラムを作って異なる魔方陣の数を計算してあげましょう。
しかし、その数は非常に多いことが予想されるので、ある素数 M で割った余りを求めることにします。

なお、ある魔方陣 G と H が異なるとは、あるそーるじぇむ v について G と H でその色が異なる、もしくはあるそーるじぇむの組 u, v が G と H の一方でのみ接続されていることを指します。

入力形式

N M

制約

2 \leq N \leq 2,000

10^8 \leq M \leq 10^9 + 7

M は素数であることが保証される

出力形式

条件を満たすグラフの個数を M で割った余りを整数で 1 行に出力してください。

入力例1

3 310779401

出力例1

12

例えば、以下のような魔方陣は条件を満たします。

入力例2

7 566666561

出力例2

98638848

例えば、以下のような魔方陣は条件を満たします。

入力例3

1010 1000000007

出力例3

862232855

M で割った余りを出力することに注意してください。

Solution in C (judge verdict: Accepted)

#include <stdio.h>
#define ll long long
#define rep(i,l,r)for(ll i=(l);i<(r);i++)
ll pom(ll a,ll n,int m){ll x=1;for(a%=m;n;n/=2)n&1?x=x*a%m:0,a=a*a%m;return x;}
#define invp(a,p)pom(a,p-2,p)

//階乗,二項係数
//*
ll fact[100010];
ll invfact[100010];
void makefact(int n,ll m){
	fact[0]=1;
	rep(i,0,n)fact[i+1]=fact[i]*(i+1)%m;
	invfact[n]=invp(fact[n],m);
	for(int i=n;i>0;i--)invfact[i-1]=invfact[i]*i%m;
}
ll choose(int n,int r,int m){return n<0||r<0||n-r<0?0:fact[n]*invfact[r]%m*invfact[n-r]%m;}
//*/

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	makefact(2010,m);
	ll ans=0;
	rep(i,1,n){
		ll temp=0;
		rep(j,0,n-i){
			//赤がi個で、黒n-i個のうちj個が赤と繋がっていない
			ll aaa=pom(pom(2,(n-i-j),m)-1,i,m)*choose(n-i,j,m)%m;
			temp+=j%2?-aaa:aaa;
		}
		ans+=temp%m*pom(2,i*(i-1)/2,m)%m*pom(2,(n-i)*(n-i-1)/2,m)%m*choose(n,i,m)%m;
	}
	printf("%lld\n",(ans%m+m)%m);
}