【C++】JOISC 2020 Day2原题+翻译+解析+代码

T1 Chameleon

原题

CSDN下载:https://download.csdn.net/download/Ljnoit/12260005

链接

LOJ-3274
UOJ-504
vjudge

翻译

题目描述

在 JOI 动物园中,有着 2 N 2N 只变色龙,编号为 1 2 N 1…2N 。其中,有 N N 只变色龙的性别为 X X ,其余 N N 只的性别为 Y Y

每只变色龙都有一个原色。关于原色的可公开情报如下:

  • 所有性别为 X X 的变色龙的原色不同。
  • 对于每只性别为 X X 的变色龙,都存在唯一的原色与其相同的变色龙,且性别为 Y Y

现在,JOI 动物园迎来了恋爱的季节。每只变色龙都爱上了另一只变色龙。关于恋爱对象的可公开情报如下:

  • 每只变色龙都很专一于唯一一只异性的变色龙。
  • 一只变色龙和它的恋爱对象的原色不同。
  • 不存在两只变色龙同时追求另一只变色龙。

你可以召集一部分变色龙来组织一场会议。对于一只参加会议的变色龙 s s ,令 t t 为它的恋爱对象。 s s 肤色由以下方式决定:

  • 如果 t t 参加了这场会议,则 s s 的肤色为 t t 的原色。
  • 如果 t t 没参加这场会议,则 s s 的肤色为 s s 的原色。

一只变色龙的肤色可以在不同的会议间发生改变。对于你组织的一场会议,你可以得到场上所有变色龙的肤色的种类数。

由于变色龙也会感到厌烦,所以你最多只能组织 20000 20000 场会议。同时你需要根据你得到的信息,确定有哪些变色龙的原色相同。
请你写一个程序在组织不超过 20000 20000 场会议的前提下确定所有相同原色的变色龙。

实现细节

你需要提交一个文件。
这个文件的名字应为 chameleon.cpp。这个程序应当包含 chameleon.h,且其中需要实现以下函数:

  • void Solve(int N)
    对于每组测试数据,保证这个函数会被恰好调用一次。
    • 其参数 N 为题目中的 N N ,性别为 $X 的变色龙的个数。

你的程序可以调用以下函数:

  • int Query(const std::vector &p)
    你可以通过调用这个函数组织一场会议。

    • 参数 p p 是参加这场会议的变色龙的列表。
    • 返回值即为本场会议所有变色龙的肤色的种类数。
    • p p 中的每个元素都应该是一个 [ 1 , 2 N ] [1,2N] 内的整数,否则你的程序将会被判为 Wrong Answer [1]
    • p p 中的元素不得重复,否则你的程序将会被判为 Wrong Answer [2]
    • 你的程序不应调用 Q u e r y Query 函数超过 20000 20000 次,否则你的程序将会被判为Wrong Answer [3]
  • void Answer(int a, int b) 你可以通过调用这个函数回答一对原色相同的变色龙。

    • 参数 a a b b 表示变色龙 a , b a,b 的原色相同。
    • 必须保证 1 a , b 2 N 1≤a,b≤2N ,否则你的程序将会被判为 Wrong Answer [4]
    • 你的程序不得以相同的 a 值或 b 值调用此函数两次及以上,否则你的程序将会被判为 Wrong Answer [5]
    • 如果 a,b 的原色不同,你的程序将会被判为 Wrong Answer [6]
      你的程序应当调用 Answer 函数恰好 N 次,否则你的程序将会被判为 Wrong Answer [7]
实现提示
  • 你的程序可以实现其他函数供内部使用,或使用全局变量。
  • 你的程序不得访问标准输入输出流,也不得通过任何方法访问其他文件。不过,你的程序可以输出到标准错误流。

编译与运行

你可以在附加文件中下载到一个压缩文件,其中包含一个样例交互库以测试你的程序。这个压缩文件也包含了一个你应当提交的程序的样例。
样例交互库为 grader.cpp。为了测试你的程序,请把 grader.cpp,chameleon.h,chameleon.cpp 放在同一目录下,并执行以下命令来编译你的程序:
g++ -std=gnu++14 -O2 -o grader grader.cpp chameleon.cpp
若编译成功,会在当前目录生成一个可执行文件 grader
请注意样例交互库并非实际交互库。样例交互库仅是由一个单一的,从标准输入流读入数据,并将结果输出到标准输出流的过程构成的。

输入格式

样例交互库从标准输入流读入以下数据:

第一行,一个整数 N N
第二行, 2 N 2N 个整数 Y i Y_i ,表示每只变色龙的性别,若其为 0 0 则性别为 X X ,否则为 Y Y
第三行, 2 N 2N 个整数 C i C_i ,表示每只变色龙的原色,保证其在 [ 1 , N ] [1,N] 内。
第四行, 2 N 2N 个整数 L i L_i ,表示每只变色龙的恋爱对象。

输出格式

样例交互库向标准输入流输出以下数据:

若你的程序是正确的,样例交互库将以 Accepted: 100 的形式输出你调用 Query 的次数。
若你的程序被判为 Wrong Answer,样例交互库将以 Wrong Answer [1] 的形式输出其类型。
若你的程序触发了不止一种 Wrong Answer,样例交互库只会输出其中一种。

样例输入 1

4
1 0 1 0 0 1 1 0
4 4 1 2 1 2 3 3
4 3 8 7 6 5 2 1

样例调用

调用 子调用 返回值
Solve(4)
Query([]) 0
Query([6, 2]) 2
Query([8, 1, 6]) 2
Query([7, 1, 3, 5, 6, 8]) 4
Query([8, 6, 4, 1, 5]) 3
Answer(6, 4)
Answer(7, 8)
Answer(2, 1)
Answer(3, 5)

在附加文件中,sample-02.txt 满足第一个子任务的限制,sample-03.txt 满足第四个子任务的限制。

数据范围与提示

对于所有数据,满足:

  • 2 N 500 2≤N≤500
  • 0 Y i 1 ( 1 i 2 N ) 0≤Y_i≤1 (1≤i≤2N)
  • 1 C i N ( 1 i 2 N ) 1≤C_i≤N (1≤i≤2N)
  • 对于每个 j ( 1 j N ) j (1≤j≤N) ,存在一个唯一的 i ( 1 i 2 N ) i (1≤i≤2N) 满足 Y i = 0 , C i = j Y_i=0,C_i=j
  • 对于每个 j ( 1 j N ) j (1≤j≤N) ,存在一个唯一的 i ( 1 i 2 N ) i (1≤i≤2N) 满足 Y i = 1 , C i = j Y_i=1,C_i=j
  • 1 L i 2 N ( 1 i 2 N ) 1≤L_i≤2N (1≤i≤2N)
  • Y i Y L i ( 1 i 2 N ) Y_i≠Y_{L_i} (1≤i≤2N)
  • C i C L i ( 1 i 2 N ) C_i≠C_{L_i} (1≤i≤2N)
  • L k L l ( 1 k < l 2 N ) L_k≠L_l (1≤k<l≤2N)

详细子任务附加条件及分值如下表:

子任务编号 附加限制 分值
1 L L i = i ( 1 i 2 N ) L_{L_{i}}=i(1\le i\le 2N) 4
2 N 7 N≤7 20
3 N 50 N≤50 20
4 Y i = 0 ( 1 i N ) Y_i=0 (1≤i≤N) 20
5 - 36

解析

听说有一种奇怪的方法是输出1,但我们不希望这样做。

首先有一个暴力的做法,将任意两个点判断,可以得到与之相关的1或3只变色龙:
1只是两只变色龙相互喜欢,那么剩下那只就是颜色相同;
3只从3只选2只并和自己判断一次,结果为1的那次剩下的那个就是他喜欢的,然后将所有喜欢关系删掉后剩下的就是颜色相同。

但这样一开始需要 O ( n 2 ) O(n^2) 次的判断,考虑优化,如果将点划分成若干个集合,每一个集合内部没有特殊关系就可行了,然后就可以再集合中二分来查找了,那么直接对前 i 1 i-1 个点构成的图染成 2 2 种颜色( 4 4 种颜色容易超过次数),分别进行二分查找即可,次数是 O ( n l o g n ) O(nlogn) 的,常数要注意( 4 4 种颜色的要注意要取编号最小的颜色,不然会被卡)

代码

#include "chameleon.h"
#include <bits/stdc++.h>

#define RI                 register int
#define re(i,a,b)          for(RI i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

const int N=1005;

vector<int> v,p[11],vec[N];
int vis[N],to[N],ans[N][2];

bool pd(vector<int> &k,int l,int r,int x) {
    p[4].clear();
    p[4].push_back(x);
    for(int i=l; i<=r; i++) p[4].push_back(k[i]);
    return Query(p[4])<p[4].size();
}

void find(vector<int> &a,int l,int r,int i) {
    int rr=r;
    while(1) {
        r=rr;
        if((vec[i].size()==3) || (!pd(a,l,r,i))) return;
        while(l<r) {
            int mid=(l+r)>>1;
            if(pd(a,l,mid,i)) r=mid;
            	else l=mid+1;
        }
        vec[i].push_back(a[l]);
        vec[a[l]].push_back(i);
        l++;
    }
}

void Solve(int n) {
    n=n<<1;
    for(int i=1; i<=n; i++) {
        int flag=4;
        for(int j=0; j<4; j++)
            if((vec[i].size()==3) || (!pd(p[j],0,p[j].size()-1,i))) flag=MIN(flag,j);
            	else find(p[j],0,p[j].size()-1,i);
        p[flag].push_back(i);
    }
    int t=0;
    for(int i=1; i<=n; i++) {
        if(vec[i].size()<3) continue;
        vis[i]=1;
        for(int j=0; j<2; j++)
            for(int k=j+1; k<3; k++){
                v.clear();
                v.push_back(i);
                v.push_back(vec[i][j]);
                v.push_back(vec[i][k]);
                if(Query(v)==1) {
                    to[i]=vec[i][3-j-k];
                    j=k=3;
                }
            }
        if(!to[i]) to[i]=vec[i][0];
    }
    for(int i=1; i<=n; i++)
        if(vis[i]) {
            for(int j=0; j<3; j++)
                if(vec[i][j]==to[i]) vec[i][j]=0;
            for(int j=0; j<3; j++)
                if(vec[to[i]][j]==i) vec[to[i]][j]=0;
        }
    for(int i=1; i<=n; i++)
        if(vis[i]) for(int j=0; j<3; j++)
            if((vec[i][j]) && (vec[i][j]<i) && (vec[vec[i][j]].size()==3)) {
                ans[++t][0]=i;
                ans[t][1]=vec[i][j];
            }
    for(int i=1; i<=n; i++)
        if(vec[i].size()==1) {
            if((vec[vec[i][0]].size()==1) && (i>vec[i][0])) continue;
            ans[++t][0]=i;
            ans[t][1]=vec[i][0];
        }
    for(int i=1; i<=(n>>1); i++) Answer(ans[i][0],ans[i][1]);
}

T2 Joitter2

原题

CSDN下载:https://download.csdn.net/download/Ljnoit/12260005

链接

LOJ-3275
UOJ-505
vjudge

翻译

题目描述

Joitter 是一款社交软件,你可以在这里和你的朋友分享你的高光时刻。

在 Joitter 中,你可以关注别的用户。举例来说,当用户 a 关注了另外一个用户 b ,用户 a 可以在时间轴上阅读用户 b 的帖子。在这种情况下,用户 b 有可能关注用户 a ,也可能不关注用户 a 。当然,用户 a 不能关注 Ta 自己或者关注用户 b 超过一次。

一共有 N 个用户已经开始使用 Joitter ,一开始他们没有关注任何其他用户。

从现在起,持续 M 天,在第 i 天会发生用户 Ai 关注用户 Bi 的事件(1≤i≤M)。

Joitter 官方正在计划在这 M 天中举行一场活动,这场活动有如下的步骤:

选择一个用户 x 。
同时选择一个被 x 关注的用户 y 。
选择一个用户 z ,要求满足 z 不是 x ,x 没有关注 z ,且 y 和 z 互相关注。
让 x 关注 z
重复上述步骤,直到无法选出三元组 (x,y,z)
Joitter 官方仍然还没有决定何时开始举办这个活动。所以他们想要知道,∀i∈[1,m],若活动在第 i 天开始,活动结束后每个用户关注其他用户数量和的最大值是多少。

输入格式

从标准输入中读入以下内容:

第一行两个整数 N,M;

接下来 M 行,每行两个整数 Ai,Bi。

输出格式

输出 M M 行到标准输出,第 i 行输出若活动在第 i 天开始,活动结束后每个用户关注其他用户数量和的最大值是多少。

样例输入 1

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

样例输出 1

1
2
4
4
5
9

样例说明 1

第一天,用户 1 关注了用户 2。在这天活动结束的话,没有任何其他用户会关注其他人。所以总和是 1。

第二天,用户 2 关注了用户 3。在这天活动结束的话,没有任何其他用户会关注其他人。所以总和是 2。

第三天,用户 3 关注了用户 2。在这天活动结束的话,用户 1 会关注用户 3。所以总和是 4,并且它是总和的可能最大值。

第四天,用户 1 关注了用户 3。在这天活动结束的话,没有任何其他用户会关注其他人。所以总和是 4。

第五天,用户 3 关注了用户 4。在这天活动结束的话,没有任何其他用户会关注其他人。所以总和是 5。

第六天,用户 4 关注了用户 3。在这天活动结束的话,用户 1 会关注用户 4,用户 2 会关注用户 4,用户 4 会关注用户 2。所以总和是 9,并且它是总和的可能最大值。

样例输入 2

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

样例输出 2

1
2
3
4
5
7
11
17
25
30

数据范围与提示

对于所有数据, 2 N 1 0 5 2≤N≤10^5 , 1 M 3 × 1 0 5 1≤M≤3×10^5 ,保证:

  • 1 A i , B i N ( 1 i M ) 1≤A_i,B_i≤N (1≤i≤M)
  • A i B i ( 1 i M ) A_i≠B_i (1≤i≤M)
  • ( A i , B i ) ( A j , B j ) ( 1 i < j M ) (A_i,B_i)≠(A_j,B_j) (1≤i<j≤M)

详细子任务及附加限制如下表:

子任务编号 附加限制 分值
1 N≤50 1
2 N≤2×103 16
3 无附加限制 83

解析

方法:并查集+STL+启发式合并
简单地说就是加入一条边x->y。如果x这个点到y并查集已经有边了,那么这条边就不用加入了。所以维护一个set表示x这个点能到哪些并查集。如果y并查集到x的并查集有边,那么x,y应该merge。

代码

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <vector>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

namespace IO {
	#include <cctype>

	template <typename T>
	inline void read(T &x){
		x=0; 
		char c=0; 
		T w=0;  
		while (!isdigit(c)) w|=c=='-',c=getchar();  
		while (isdigit(c)) x=x*10+(c^48),c=getchar();  
		if(w) x=-x;  
	}
	
	template <typename T>
	inline void write(T x) {
	    if(x<0) putchar('-'),x=-x;
	    if(x<10) putchar(x+'0');
	        else write(x/10),putchar(x%10+'0');
	}
	
	template <typename T>
	inline void writeln(T x) {
	    write(x);
	    putchar('\n');
	}
} 

using IO::read;
using IO::write;
using IO::writeln;

const int N=1e5+5;

int fa[N],tot[N]; 

set<int> r[N],c[N]; 
set<int>::iterator it;

inline int getfa(int x) {
	return fa[x]==x ? x : fa[x]=getfa(fa[x]);
}

inline void SWAP(int &a,int &b) {
	a=a^b;
	b=a^b;
	a=a^b;
}

LL ans;

void merge(int x,int y) {
	if(x==y) return;
	if(r[x].size()+c[x].size()>r[y].size()+c[y].size()) 
		SWAP(x,y);
	ans-=1LL*tot[x]*r[x].size()+(LL)tot[y]*r[y].size();
	vector<int>vec;
	for(it=r[x].begin(); it!=r[x].end(); it++) {
		int z=getfa(*it);
		if(c[y].count(z))vec.push_back(z);
		c[z].erase(x),c[z].insert(y);
		r[y].insert(*it);
	}
	for(it=c[x].begin(); it!=c[x].end(); it++) {
		int z=getfa(*it);
		if(c[*it].count(y))
			vec.push_back(*it);
		c[y].insert(*it);
	}
	tot[y]+=tot[x],fa[x]=y;
	ans+=1LL*tot[y]*r[y].size();
	for(int i=0; i<vec.size(); i++)
		merge(getfa(y),getfa(vec[i]));
}

int main() {
	int n,m,x,y;
	read(n);
	read(m);
	for(int i=1; i<=n; i++)
		fa[i]=i,tot[i]=1,r[i].insert(i);
	while(m--) {
		scanf("%d%d",&x,&y);
		int fx=getfa(x),fy=getfa(y);
		if(fx!=fy) {
			if(c[fy].count(fx)) merge(fx,fy);
				else if(!r[fy].count(x))
					c[fx].insert(fy),r[fy].insert(x),ans+=tot[fy];
		}
		writeln(ans);
	}
	return 0;
}

T3 Ruins3

原题

CSDN下载:https://download.csdn.net/download/Ljnoit/12260005

链接

LOJ-3276
UOJ-506
vjudge

翻译

题目描述

JOI 教授是 IOI 国有名的历史学家。他在研究 IOI 国一个古庙时发现了石柱的遗迹以及一篇古 IOI 国人写的文档。 在文档中,给出了这些石柱的相关描述,具体如下:

  • 刚建好时,庙里有 2 N 2N 根石柱,编号为 1…2N。对于任意 k [ 1 , N ] k∈[1,N] ,恰好有两根石柱的高度为 k。
  • 随后发生了 N N 次地震,损坏了某些石柱,每次损坏将使石柱的高度减一。由于古人类的保护,其他石柱未被损坏,高度保持不变。
  • 地震发生时,对于任意 k [ 1 , N ] k \in [1,N] ,古人类只能保护恰好一根高度为 k 的石柱。如果有多根石柱高度相同,根据古人类达成的共识,他们将选择保护编号最大的那一根。也就是说,如果石柱 i 在地震前高度是 hi,古人类会去选择保护这根石柱当且仅当 h i 1 h_i≥1 且任意 j > i j>i 满足 h j h i h_j≠h_i
  • N N 次地震后,只剩下 N N 根石柱了,即只有 N N 根石柱的高度至少为 1 1
    JOI 教授觉得如果他能还原出来这些石柱地震前的高度,他能搞个大新闻。在他更仔细的研究后,发现 N N 次地震后留下来的石柱的编号为 A 1 , A 2 , , A N A_1,A_2,…,A_N 。他想知道原来的高度组合有多少种可能。因为你是 JOI 教授的学徒(pupil),他想让你写个程序计算这个方案数。

你的任务是编写一个程序,给出 N N 次地震后留下来的石柱的编号,计算初始时 2 N 2N 根石柱的高度组合种数模 1 0 9 + 7 10^9+7

输入格式

第一行一个整数 N N

接下来一行 N N 个空格分隔的整数 A 1 , , A n A_1,…,A_n

输出格式

输出题面描述中所求的方案数模 1 0 9 + 7 10^9+7 的值。

样例输入 1

3
3 4 6

样例输出 1

5

样例解释 1

假设初始时石柱的高度为 ( 2 , 2 , 3 , 3 , 1 , 1 ) (2,2,3,3,1,1) 。因为对于 k [ 1 , 3 ] k∈[1,3] 的各个高度都恰好有 2 2 根石柱,因此符合题面描述中的条件。

  • 第一次地震时,石柱 2 , 4 , 6 2,4,6 被古人类保护。地震后,石柱高度变为 ( 1 , 2 , 2 , 3 , 0 , 1 ) (1,2,2,3,0,1)
  • 第二次地震时,石柱 3 , 4 , 6 3,4,6 被古人类保护。地震后,石柱高度变为 ( 0 , 1 , 2 , 3 , 0 , 1 ) (0,1,2,3,0,1)
  • 第三次地震时,石柱 3 , 4 , 6 3,4,6 被古人类保护。地震后,石柱高度变为 ( 0 , 0 , 2 , 3 , 0 , 1 ) (0,0,2,3,0,1)

三次地震后,石柱 3 , 4 , 6 3,4,6 留下来了,与输入一致。
除了这个例子,可能的高度组合还有 ( 2 , 3 , 2 , 3 , 1 , 1 ) , ( 2 , 3 , 3 , 2 , 1 , 1 ) , ( 3 , 2 , 2 , 3 , 1 , 1 ) , ( 3 , 2 , 3 , 2 , 1 , 1 ) (2,3,2,3,1,1), (2,3,3,2,1,1), (3,2,2,3,1,1), (3,2,3,2,1,1)

因此,总共有 5 5 种高度组合符合输入数据和题面描述的条件。

样例输入 2

1
1

样例输出 2

0

样例解释 2

本输入的唯一可能高度组合为 (1,1)。地震后,石柱高度变为 (0,1)。

因此,没有符合输入数据和题面描述给出的条件的初始高度组合。

样例输入 3

10
5 8 9 13 15 16 17 18 19 20

样例输出 3

147003663

样例解释 3

总共有 111 147 004 440 种符合条件的初始高度组合,将这个数除 109+7 余数为 147 003 663,即输出值。

数据范围与提示

对于 100 100 % 的数据,有 1 N 600 , 1 A i 2 N ( 1 i N ) , A i < A i + 1 ( 1 i N 1 ) 1≤N≤600, 1≤A_i≤2N(1≤i≤N), A_i<A_{i+1}(1≤i≤N−1)

各子任务详情如下:

子任务编号 分值 特殊限制
1 6 N≤13
2 52 N≤60
3 42 无特殊限制

解析

方法:计数dp

从大到小枚举每个点,看看每个点会停在哪儿。
状态转移方程详见代码。

代码

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <vector>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

namespace IO {
	#include <cctype>

	template <typename T>
	inline void read(T &x){
		x=0; 
		char c=0; 
		T w=0;  
		while (!isdigit(c)) w|=c=='-',c=getchar();  
		while (isdigit(c)) x=x*10+(c^48),c=getchar();  
		if(w) x=-x;  
	}
	
	template <typename T>
	inline void write(T x) {
	    if(x<0) putchar('-'),x=-x;
	    if(x<10) putchar(x+'0');
	        else write(x/10),putchar(x%10+'0');
	}
	
	template <typename T>
	inline void writeln(T x) {
	    write(x);
	    putchar('\n');
	}
} 

using IO::read;
using IO::write;
using IO::writeln;

const int N=605;
const int mod=1e9+7;
const int inv2=(mod+1)>>1;

int fl[N],gl[N],ff[N],gg[N],f[N],g[N],c[N][N];
bool vis[2005];

int main() {
	int n,x;
	read(n);
	for(int i=1; i<=n; i++)
		read(x),vis[x]=1;
	c[0][0]=1;
	for(int i=1; i<=n; i++) {
		c[i][0]=1;
		for(int j=1; j<=n; j++) 
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
	}
	fl[0]=gg[0]=1;
	for(int i=1; i<=n; i++) {
		memcpy(gl,fl,sizeof(fl));
		memset(fl,0,sizeof(fl));
		for(int j=0; j<=i; j++) if(gl[j]) {
			if(j>=1) fl[j-1]=(fl[j-1]+1LL*gl[j]*inv2%mod*inv2)%mod;
			fl[j]=(fl[j]+gl[j])%mod;
			fl[j+1]=(fl[j+1]+gl[j])%mod;
		}
		gg[i]=fl[0];
	}
	int jx=1;
	for(int i=1; i<=n; i++)
		jx=1LL*jx*i%mod,gg[i]=1LL*gg[i]*jx%mod,ff[i]=gg[i];
	ff[0]=gg[0];
	for(int i=1; i<=n; i++) {
		for(int j=1; j<i; j++)
			ff[i]=(ff[i]+1LL*(mod-ff[j])*gg[i-j]%mod*c[i-1][j-1])%mod;
	}
	f[0]=1;
	int nw=0,nn=0;
	for(int i=(n<<1); i; i--) {
		memcpy(g,f,sizeof(f));
		memset(f,0,sizeof(f));
		if(vis[i]) {
			for(int j=0; j<=nn; j++) if(g[j]) {
				f[j]=(f[j]+g[j])%mod;
				for(int j1=1; j+j1<=nn+1; j1++) {
					f[j+j1]=(f[j+j1]+1LL*g[j]*ff[j1]%mod*c[nn-j][j1-1])%mod;
				}
			}
			nn++;
		} else {
			for(int j=0; j<=nn; j++)
				f[j]=(f[j]+1LL*g[j]*(j-nw))%mod;
			nw++;
		}
	}
	writeln(f[n]);
	return 0;
}

发布了106 篇原创文章 · 获赞 156 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Ljnoit/article/details/105262635