1月27日
Description
现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对 \(10^9+7\) 取模。
Input
输入文件名为count. in。
第一行包括三个整数n,m,k。
接下来m行,每行两个整数u,v,描述一个矛盾关系(u,v)。
保证不存在两对矛盾关系(u,v),(x,y),使得u=x且v=y 。
Output
输出文件名为count.out。
输出包括一行表示合法的排列数。
Sample Input
输入1:
4 2 1
1 3
4 2
输入2:
10 12 3
2 6
6 10
1 7
4 1
6 1
2 4
7 6
1 4
10 4
10 9
5 9
8 10
Sample Output
输出1:
18
输出2:
123120
Data Constraint
对于30%的数据,\(n\leq 10\)
对于60%的数据,\(n\leq 15\)
对应100%的数据,n, \(k\leq 20\) ,\(m\leq n \times (n-1)\),保证矛盾关系不重复。
Solution
- 【我的解法】
无脑暴力,30分,可以欣赏一下
#include<cstdio>
#pragma GCC optimize(3)//咳咳,别管这是什么
using namespace std;
const int mod=1000000007;
int n,m,k,tot;
int g[21][21];
int a[21];
bool vis[21];
void dfs(int x)
{
if(x>n)
{
int cnt=0;
for(int i=1;i<n;++i)
for(int j=i+1;j<=n;++j)
cnt+=g[a[i]][a[j]];
if(cnt<=k)
{
tot++;
tot%=mod;
}
return;
}
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
a[x]=i;
vis[i]=true;
dfs(x+1);
vis[i]=false;
}
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
g[v][u]=1;
}
dfs(1);
printf("%d",tot);
fclose(stdin);
fclose(stdout);
return 0;
}
- 【大佬正解】
首先把状态压缩一下,把队伍压缩成一个二进制,由于n最大只有20,所以 \(2^{20}\) 是不会MLE的。
然后从前往后递推。f(s,i)表示当前选了s集合这些人,i表示违反了多少条矛盾关系。
然后如果暴力转移的话,是会炸的。 \(O(2^nn^2k)\)。所以要进行一些非(sang)常(xin)基(bing)础(kuang)的优化。
因为矛盾是没有重复的,所以把要在x前面的连个链式前向星,然后往左位移再&一下就可以了。
(其实有更优方法,但我不会调。。。只要维护一个fal[x]
表示要排在x后面的人的集合,那么每次只要求出s & fal[x]
的1的个数就能快速更新𝑖了。)
#include<cstdio>
//#pragma GCC optimize(3)//不敢了不敢了再也不敢吸臭氧了
#define mod 1000000007
using namespace std;
int n,m,k,tot,ans;
int f[2097153][21];
int head[21],to[401],next[401],dive;
void add(int x,int y)
{
++dive;
next[dive]=head[x];
to[dive]=y;
head[x]=dive;
}
inline int read()//不吸臭氧就只能快读了QAQ
{
int x=0; char c=getchar();
while (c<'0' || c>'9') c=getchar();
while (c>='0' && c<='9')
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x;
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
n=read();
m=read();
k=read();
for(int i=1,u,v;i<=m;i++)
{
u=read();
v=read();
add(v,u);
}
f[0][0]=1;
tot=(1<<n)-1;
for(int s=0;s<=tot;s++)
for(int i=0;i<=k;i++)
{
if(!f[s][i]) continue;
for(int x=1;x<=n;x++)
{
if(s&(1<<x-1)) continue;
int c=0;
for(int p=head[x];p;p=next[p])
if(!(s&(1<<to[p]-1))) c++;
if(i+c>k) continue;
f[s|(1<<x-1)][i+c]+=f[s][i];
f[s|(1<<x-1)][i+c]%=mod;
}
}
for(int i=0;i<=k;i++)
{
ans+=f[tot][i];
ans%=mod;
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}