题目大意
给定一些形如 的等式,问有几种在这些等式之间添加符号( )的方法使得不等式成立。
思路
首先肯定可以想到并查集,将所有相等的数合并到一起考虑。
再可以想到
其实是没有用的,因为大于号不可能和小于号同时发生(
的形式就已经把等式锁死了)。
那么问题就转化成了,一些不同的数,在他们之间安排上等于号和小于号的方案数。
将
的意义转化为“分组”,问题就又转化成了:将
个不同的数划分成有区别的若干组的方案数(组与组之间使用
号连接,故组的顺序是要被考虑的)”
考虑DP,
表示前
个数分成
个不同的组的方案数。
考虑转移,第
个数有两种方式被放入
- 插入到前面的某一组中,方案数 ,因为前面已经有 组
- 自成一组,并插入到某个地方,方案数 ,因为原先 组会有 个空位置让你插入。
另一种思路
并查集依然是并查集,将一堆数缩成一个数。
依然是那个DP的意义,考虑将
个数划分成有区别的若干组的方案数。
再回想斯特林数的组合意义:将
个球装入
个不可区分的盒子的方案数。
这两个有什么区别呢?一个盒子有区别,一个无区别。那么就是相差了一个排列。
答案为
代码(DP)
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cctype>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back
using namespace std;
inline ll read()
{
long long f=1,sum=0;
char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
const int MAXN=20;
int fa[MAXN];
int find(int x)
{
if (fa[x]!=x)
fa[x]=find(fa[x]);
return fa[x];
}
pa get_num(string s)
{
int pos=0;
for (int i=0;i<s.size()&&!pos;i++)
if (s[i]=='=')
pos=i;
pa re;
for (int i=0;i<pos;i++)
re.fi=re.fi*10+s[i]-'0';
for (int i=pos+1;i<s.size();i++)
re.se=re.se*10+s[i]-'0';
re.fi++,re.se++;
return re;
}
ll f[MAXN][MAXN];
struct PossibleOrders{
long long howMany(int num,vector<string> facts)
{
for (int i=1;i<=num;i++)
fa[i]=i;
vector<string>::iterator it;
for (it=facts.begin();it!=facts.end();it++)
{
pa nums=get_num(*it);
int f1=find(nums.fi),f2=find(nums.se);
if (f1!=f2) fa[f1]=f2;
}
int tot=0;
f[0][0]=1;
for (int i=1;i<=num;i++)
{
if (find(i)!=i) continue;
tot++;
for (int j=1;j<=tot;j++)
f[tot][j]=(f[tot-1][j-1]+f[tot-1][j])*j;
}
ll ans=0;
for (int i=1;i<=tot;i++)
ans+=f[tot][i];
return ans;
}
};
代码(斯特林数)
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cctype>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back
using namespace std;
inline ll read()
{
long long f=1,sum=0;
char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
const int MAXN=20;
int fa[MAXN];
int find(int x)
{
if (fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
pa get_num(string s)
{
int pos=0;
for (int i=0;!pos;i++)
if (s[i]=='=')
pos=i;
pa re;
for (int i=0;i<pos;i++)
re.fi=re.fi*10+s[i]-'0';
for (int i=pos+1;i<s.size();i++)
re.se=re.se*10+s[i]-'0';
re.fi++,re.se++;
return re;
}
long long S[MAXN][MAXN];
long long fac[MAXN];
struct PossibleOrders{
long long howMany(int num,vector<string> facts)
{
vector<string>::iterator it;
for (int i=1;i<=num;i++)
fa[i]=i;
for (it=facts.begin();it!=facts.end();it++)
{
pa nums=get_num(*it);
int f1=find(nums.fi),f2=find(nums.se);
if (f1!=f2)
fa[f1]=f2;
}
int n=0;
for (int i=1;i<=num;i++)
if (find(i)==i)
n++;
fac[0]=1;
for (int i=1;i<=n;i++)
fac[i]=fac[i-1]*i;
S[1][1]=1;
for (int i=2;i<=n;i++)
for (int j=1;j<=i;j++)
S[i][j]=S[i-1][j]*j+S[i-1][j-1];
ll ans=0;
for (int i=1;i<=n;i++)
ans+=fac[i]*S[n][i];
return ans;
}
};