题意:给定一个数n,构造一个数组包含1到n这n个数,其中每一个数ai两侧最近的大于ai的数与ai相连,使这个序列中出现环。问可能的序列多少种。
想到了可以构造最小的环即三个数且两大夹一小,然后通过排列组合计数,但很难或者说不能做出来。其实自己做题时如果能够尽快找到为什么得不出来或许也就能够想到反过来考虑了。其实这个题目做法是先算出全部的排列然后减去不符合条件的(这个好找),不符合条件即严格不会出现两大夹一小情况,由大到小除了最大的数外每个数都可以在最大的数(或者说比它大的数构成的序列)左边或右边,所以是2^n-1种。答案即n - 2 ^ ( n - 1 )。
其实都不想亮代码了,因为代码不需要水平而我写的也完全没什么水平,不过还是加上完整。
#include<iostream>
#include<iomanip>
#include<fstream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<map>
#include<queue>
#include<vector>
#include<utility>
#include<ostream>
#include<istream>
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
#define PI acos(-1.0)
const ll maxn=1*1e6;
const int mx=(1<<20)+99;
ll n,m,x,ans,aans,flag,sybl,cc,aa;
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
//int t;
//cin>>t;
//for(int o=1;o<=t;++o)
//{
cin>>n;
aa=1;
for(int i=1;i<=n-1;i++)
{
aa*=2;
aa%=mod;
}
ans=1;
for(int i=1;i<=n;i++)
{
ans*=i;
ans%=mod;
}
cout<<((ans>=aa?ans:ans+mod)-aa)%mod<<endl;
//}
//return 0;
}
总结:
这样说着好像题目很简单,当然讲真的这个题也没那么难,不过确实不看题解不知道怎么做。当一个问题正向或者求一个问题的局部很难解很复杂时,可以反过来考虑或者是这里的全局减去恰好相反的,总之还是一种相反的。这一思路大部分都知道,甚至绝对是高中排列组合最基本的东西之一,但真到了用的时候(不再是排列组合那一套,但仍可能带着排列组合的神)还是俩字“不会”啊。对于这个题究其原因两点:这方面的知识没有过专门的复习和相应的训练,题目不敏感知识有遗忘。更重要的是,这种反向思考的能力没有培养起来,即使是其它算法的讲解中老师也强调过这个的,但讲的时候明白都懂,做的时候想不到,看了题解又恍然对啊这个题就这样。所以,这个补题与其说补这个题倒不如说补这个思维方式,这个能力的养成更重要,而方法就是多做题多吃亏,吃多了就记住什么味道了。