牛客萌新赛5 - G
题目大意:给你一个长度为n的至少有n-1个不相同元素的序列,问你其中长度为k的子序列为多少,最终答案%1e9+7。
如果两个子序列长度不同,或者长度相同但存在位置i使得两个子序列b,c,bi!=ci成立,那么它们就是不同的。
考虑:当序列中没有相同元素时,相当于从n个数中拿k个数出来组成子序列。答案就是从C(k,n)。
若序列中有两个相同元素,那上面的计算必定会重复,什么情况下会重复呢,由上述定义可知,当两个相同元素中只选了
1个,并且剩下的k-1个都是在这两个元素旁边选的,最终情况下就会重复,而这种情况个数为C(k-1,n-(r-l+1))。
l和r为相同元素的左右下标,最终答案即为C(k,n)-C(k-1,n-(r-l+1))。
ll power(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) {
ans *= a;
ans %= mod;
}
a *= a;
a %= mod;
b >>= 1;
}
return ans;
}
const int M = 1e5 + 10;
struct Combinatorial_number{
int fact[M], finv[M]; // fact[x]是x的阶乘,inv[x]是fact[x]的逆元
void init(){
fact[0] = 1;
for(int i = 1; i < (int)M; i ++){
fact[i] = 1ll * fact[i - 1] * i % mod;
}
finv[M - 1] = power(fact[M - 1], mod - 2);
for(int i = M - 2; i >= 0; i --){
finv[i] = 1ll * finv[i + 1] * (i + 1) % mod;
}
}
ll C(int n, int m){
if(m < n || n < 0 || m < 0) return 0;
return 1ll * fact[m] * finv[n] % mod * finv[m - n] % mod;
}
ll A(int n, int m){
if(m < n || n < 0 || m < 0) return 0;
return 1ll * fact[m] * finv[m - n] % mod;
}
ll llC(int n, int m){
if(m < n || n < 0 || m < 0) return 0;
swap(n, m);
long long sum = 1;
if (m > n - m) m = n - m;
n = n - m + 1;
for (int i = 1; i <= m; i ++){
sum *= n ++;
sum /= i;
}
return sum;
}
};
struct Combinatorial_number comb;
void solve() {
int n, k;
cin >> n >> k;
vector<int>a(n + 1);
map<int, int>mp;
int l = -1, r = -1;
rep(i, 1, n) {
cin >> a[i];
if(mp[a[i]]) r = i, l = mp[a[i]];
else mp[a[i]] = i;
}
ll ans = comb.C(k, n);
if(r == -1) cout << ans << endl;
else {
int m = n - (r - l + 1);
ans -= comb.C(k - 1, m);
cout << (ans + mod) % mod<< endl;
}
}
SP7579 YOKOF - Power Calculus
题目大意:给你若干个n(n<=1000),初始x,可以进行自乘或除(也可以乘除中间得到的结果),问x通过最少多少次操作可以变成x^n
多组输入,n为0的时候结束。
int n;
int stk[20];
int st;
bool dfs(int now, int m, int cnt) {
if(now == n) return true; //在规定步数内找到答案
if(cnt >= m || now << (m - cnt) < n) { //剪枝,超过规定次数 若最大次幂在规定次数自乘都无法到达n
return false;
}
for (int i = st; i >= 1; i--) { //乘:从大到小
stk[++st] = now + stk[i];
if(dfs(now + stk[i], m, cnt + 1)){
return true;
}
--st;
}
for (int i = 1; i <= st; i++) { //除:从小到大
if(stk[i] >= now) continue;
stk[++st] = now - stk[i];
if(dfs(now - stk[i], m, cnt + 1)){
return true;
}
--st;
}
return false;
}
void solve() {
stk[++st] = 1;
while(cin >> n && n) {
for (int i = 0; ; i++) {
st = 1;
if(dfs(1, i, 0)) {
cout << i << endl;
break;
}
}
}
}
Addition Chains
题目大意 : 找一个与n有关的整数加成序列<a0...am>满足条件:
1.a0 = 1, 2. am = n, 3.序列严格递增, 4.对于每一个k(1~m),都有i, j(0~k-1,i,j可相等)使得a[i] + a[j] = a[k]
给定一个整数n(n <= 10000),找出符合上述四个条件的长度最小的整数加成序列。如果有多个满足要求的答案,只需要输出任意一个解即可。
要求最优的,bfs会爆空间,单纯的dfs会TLE,用迭代加深来做。
迭代加深
迭代加深搜索的本质还是深度优先搜索,只不过在搜索的同时带上了一个深度 ,当达到设定的深度时就返回。
一般用于找最优解。如果一次搜索没有找到合法的解,就让设定的深度加一,重新从根开始。
int ans[1000];
int n;
bool dfs(int now, int deg) {
if(now > deg) {
if(ans[deg] == n) return true;
else return false;
}
for (int i = now - 1; i >= 1; i--) { //优化,要最短,则从大到小开始搜
ans[now] = ans[now - 1] + ans[i];
//剪枝:当序列以两倍递增时,最后的数为最大,若此时得到的最大值都小于n,则后面怎样都不会满足
int temp = ans[now];
for (int i = now + 1; i <= deg; i++) temp *= 2;
if(temp < n) return false;
//直接返回
if(dfs(now + 1, deg)) return true;
}
return false;
}
void solve() {
ans[1] = 1;
for (int i = 2; i <= n; i++) {
if(dfs(2, i)) { //找到就输出答案直接退出,因为最先找到的就是最优解
cout << "1";
for (int j = 2; j <= i; j++) {
cout << " " << ans[j];
}cout << endl;
break;
}
}
}