51nod 最近刷题 简要题解

Posted c

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod 最近刷题 简要题解相关的知识,希望对你有一定的参考价值。

51nod 1564

由于数据是随机的,可以证明,对于每一个数,向左或右找比它小的数,长度是logn级别的

考虑枚举最大值

注意,对于每一个最大值,如果直接用2个循环枚举左右端点的话,理论是lognlogn级别的,但是还是很容易被卡的,换成贪心,用2个指针指着左右端点,每一次移动我们往数大的那个方向移动

代码:

                                            
  //File Name: nod1564.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月10日 星期一 19时59分32秒
                                   
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
const int MAXN = 100000 + 5;
const LL INF = (1<<18);
LL a[MAXN],ans[MAXN];
int l[MAXN],r[MAXN];
void solve(int n){
    l[1] = 1;
    for(int i=2;i<=n;i++){
        l[i] = i;
        while(l[i] > 1 && a[l[i]-1] <= a[i])
            l[i] = l[l[i]-1];
    }
    r[n] = n;
    for(int i=n-1;i>0;i--){
        r[i] = i;
        while(r[i] < n && a[r[i]+1] < a[i])
            r[i] = r[r[i]+1];
    }
    memset(ans,0,sizeof ans);
    for(int i=1;i<=n;i++){
        LL mi1 = a[i];
/*
        for(int j=i;j>=l[i];j--){
            mi1 = min(mi1,a[j]);
            LL mi2 = mi1;
            for(int k=i;k<=r[i];k++){
                mi2 = min(mi2,a[k]);
                ans[k-j+1] = max(ans[k-j+1],a[i] * mi2);
            }
        }
*/
        int j = i,k = i;
        while(true){
            ans[k-j+1] = max(ans[k-j+1],mi1 * a[i]);
            if(j == l[i] && k == r[i]) break;
            else if(j == l[i] && k < r[i])
                k+=1,mi1 = min(mi1,a[k]);
            else if(j > l[i] && k == r[i])
                j-=1,mi1 = min(mi1,a[j]);
            else{
                if(a[j-1] >= a[k+1])
                    j-=1,mi1 = min(mi1,a[j]);
                else
                    k+=1,mi1 = min(mi1,a[k]);
            }
        }
    }
    for(int i=n-1;i>=1;i--)
        ans[i] = max(ans[i],ans[i+1]);
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",a + i);
    solve(n);
    for(int i=1;i<=n;i++)
        printf("%lld\\n",ans[i]);
    return 0;
}
View Code

 

 

51nod 1375

给出n个数,问从中找出刚好k个数或者任意个数,使得这些数的gcd = 1的方案数

枚举gcd,mobius反演下就可以了

代码:

                                            
  //File Name: nod1375.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月10日 星期一 13时28分47秒
                                   
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#define LL long long
using namespace std;
const int MOD = 998244353;
const int MAXN = 1000000 + 1;
const int N = 100000 + 1;
int s[MAXN],K;
int mu[MAXN],prime[MAXN];
bool check[MAXN];
LL p2[N],jie[N];
LL qp(LL x,LL y){
    LL res = 1;
    for(;y>0;y>>=1){
        if(y & 1) res = res * x % MOD;
        x = x * x % MOD;
    }
    return res;
}
LL C(LL x,LL y){
    if(y < 0 || y > x) return 0;
    if(y == 0 || y == x) return 1;
    return jie[x] * qp(jie[y] * jie[x-y] % MOD,MOD - 2) % MOD;
}
void mobius(){
    memset(check,false,sizeof check);
    mu[1] = 1;
    int tot = 0;
    for(int i=2;i<MAXN;i++){
        if(!check[i]){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j=0;j<tot;j++){
            if(i * prime[j] >= MAXN) break;
            check[i * prime[j]] = true;
            if(i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }
            else{
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
void init(){
    p2[0] = jie[0] = 1;
    for(int i=1;i<N;i++){
        p2[i] = p2[i-1] * 2 % MOD;
        jie[i] = jie[i-1] * i % MOD;
    }
    mobius();
}
LL cal(int sum){
    if(K == -1) return p2[sum] - 1;
    return C(sum,K);
}
LL solve(int n,int ma){
    LL ans = 0;
    for(int k=1;k<=ma;k++){
        int sum = 0;
        for(int i=k;i<=ma;i+=k)
            sum += s[i];
        ans = (ans + mu[k] * cal(sum) + MOD) % MOD;
    }
    return ans;
}
int main(){
    init();
    int n,ma = 0;
    scanf("%d %d",&n,&K);
    for(int i=0,u;i<n;i++){
        scanf("%d",&u);
        ma = max(ma,u);
        s[u]++;
    }
    printf("%lld\\n",solve(n,ma));
    return 0;
}
View Code

 

51nod 1269

简单容斥,求组合数的时候C(n,m) % p,虽然n是10^16级别的,但是p是素数,m才20左右,直接暴力求就可以了

                                            
  //File Name: nod1269.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月10日 星期一 13时11分23秒
                                   
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
const int MOD = (int)1e9 + 7;
int inv[20];
LL a[20];
LL qp(LL x,LL y){
    LL res = 1;
    for(;y>0;y>>=1){
        if(y & 1) res = res * x % MOD;
        x = x * x % MOD;
    }
    return res;
}
LL C(LL x,LL y){
    if(x < y || y < 0) return 0;
    if(y == x || y == 0) return 1;
    LL ans = 1;
    for(int i=0;i<y;i++){
        ans = (x - i) % MOD * inv[y - i] % MOD * ans % MOD;
    }
//    printf("x = %lld y = %lld ans = %lld\\n",x,y,ans);
    return ans;
}
LL solve(int n,LL s){
    for(int i=1;i<n;i++) 
        inv[i] = qp(i,MOD - 2);
    LL ans = 0;
    for(int i=0;i<(1<<n);i++){
        LL now = 0;
        int num = 0;
        for(int j=0;j<n;j++){
            if(i & (1 << j)){
                num++;
                now += a[j] + 1;
            }
        }
        
        LL tmp = C(s - now + n -1,n - 1);
        if(num & 1) ans = (ans - tmp + MOD) % MOD;
        else ans = (ans + tmp) % MOD;
    }
    return ans;
}
int main(){
    LL s;
    int n;
    scanf("%d %lld",&n,&s);
    for(int i=0;i<n;i++) scanf("%lld",a + i);
    cout << solve(n,s) << endl;
    return 0;
}
View Code

 

51nod  1407

i表示集合的状态

f(i)表示i & x= i的x的个数,即i是x的子集

可以分治求f

然后容斥下就可以

                                            
  //File Name: nod1407.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月09日 星期日 22时04分55秒
                                   
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
const int MAXN = 1 << 20;
const int MOD = (int)1e9 + 7;
int f[MAXN];
LL p2[MAXN];
void init(){
    p2[0] = 1;
    for(int i=1;i<MAXN;i++)
        p2[i] = p2[i-1] * 2 % MOD;
}
void cal(int pre,int i){
    if(i < 0) return ;
    cal(pre,i-1);
    cal(pre+(1<<i),i-1);
    for(int j=0;j<(1<<i);j++)
        (f[pre+j] += f[pre+(1<<i)+j] )%=MOD;
}
LL solve(int n){
    cal(0,19);
//    for(int i=0;i<=10;i++)
//        cout << f[i] << endl;
    LL ans = 0;
    for(int i=0,s;i<(1<<20);i++){
        s = 0;
        for(int j=0;j<20;j++)
            if(i & (1<<j)) s++;
        LL tmp = (p2[f[i]] - 1 + MOD) % MOD;
        if(s & 1) ans = (ans - tmp + MOD) % MOD;
        else ans = (ans + tmp) % MOD;
    }
    return ans;
}
int main(){
    init();
    int n;
    scanf("%d",&n);
    for(int i=0,a;i<n;i++){
        scanf("%d",&a);
        f[a]++;
    }
    cout << solve(n) << endl;
    return 0;
}
View Code

 

51nod 1406

与上一道题一样

                                            
  //File Name: nod1406.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月09日 星期日 11时53分29秒
                                   
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int f[1<<20];
void solve(int pre,int i){
    if(i < 0) return ;
    solve(pre,i-1);
    solve(pre+(1<<i),i-1);
    for(int j=0;j<(1<<i);j++)
        f[pre+j] += f[pre+(1<<i)+j];
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0,u;i<n;i++){
        scanf("%d",&u);
        f[u]++;
    }
    solve(0,19);
    for(int i=0;i<=1000000;i++)
        printf("%d\\n",f[i]);
    return 0;
}
View Code

 

 

51nod  1362

枚举向右下方走的次数,可以得到公式

对于公式,有一问题,就是要求的是C(n,m)%p,这个时候p也可能是合数,n是10^9级别,但是m是1000级别的

注意到需要消除的数都是与p的gcd大于1的,所以只需要考虑p的质因子,将与p互质的部分直接求逆元计算,不互质的部分维护成质因子的幂次,从分子中减去即可。

O(nlogp)级别

                                            
  //File Name: nod1362.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月09日 星期日 19时14分21秒
                                   
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <vector>
#define LL long long
using namespace std;
vector<int> dive;
int s[50];
LL mod;
LL qp(LL x,LL y){
    LL res = 1;
    for(;y>0;y>>=1){
        if(y & 1) res = res * x % mod;
        x = x * x % mod;
    }
    return res;
}
LL ext_gcd(LL a,LL b,LL &x,LL &y){
    if(a == 0 && b == 0) return -1;
    if(b == 0){x = 1;y = 0;return a;}
    LL d = ext_gcd(b,a%b,y,x);
    y -= a / b * x;
    return d;
}
LL inv(LL a,LL n){
    LL x,y;
    LL d = ext_gcd(a,n,x,y);
    if(d == 1) return (x % n + n) % n;
    return -2;
}
void cal_dive(LL p){
    dive.clear();
    for(int i=2;i*i<=p;i++){
        if(p % i == 0){
            dive.push_back(i);
            while(p % i == 0) p /= i;
        }
    }
    if(p > 1) dive.push_back(p);
    sort(dive.begin(),dive.end());
}
LL fact(LL x,int f){
    for(int i=0;i<dive.size();i++){
        while(x % dive[i] == 0){
            s[i]+=f;
            x /= dive[i];
        }
    }
    if(f == -1) return inv(x,mod);
    return x;
}
LL cal(LL l,LL r,LL a,LL b,LL c){
    memset(s,0,sizeof s);
    LL ans = fact(a,-1);
    for(int i=2;i<=b;i++)
        ans = ans * fact(i,-1) % mod;
    for(int i=2;i<=c;i++)
        ans = ans * fact(i,-1) % mod;
    for(LL i=l;i<=r;i++)
        ans = ans * fact(i,1) % mod;
    for(int i=0;i<dive.size();i++){
        ans = ans * qp(dive[i],s[i]) % mod;
    }
    return ans;
}
LL solve(LL n,LL m){
    LL ans = 0;
    LL ma = min(n,m);
    cal_dive(mod);
    for(int c=0;c<=ma;c++){
        ans = (ans + cal(m-c+1,m-c+1+n,n+1,c,n-c)) % mod;
    }
    return ans;
}
int main(){
    LL n,m;
    cin >> n >> m >> mod;
    cout << solve(n,m) << endl;
    return 0;
}
View Code

 

51nod 1197

由于每次跳跃都是*2的,字符串实际上可以划分成若干条链的组合,每一条链都是logn长度的

再加个矩阵乘法

                                            
  //File Name: nod1197.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月08日 星期六 20时18分58秒
                                   
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
const int MAXN = 1000000 + 2;
const int MOD = (int)1e9 + 7;
const int N = 22;
int f[MAXN][N],len[N],g[N];
struct matrix_t{
    LL x[N][N];
    matrix_t(int v){
        memset(x,0,sizeof(x));
        for(int i=1;i<=N;i++) x[i][i] = v;
    }
    matrix_t operator*(const matrix_t &r){
        matrix_t p(0);
        for(int k=1;k<=N;k++){
            for(int i=1;i<=N;i++){
                if(x[i][k] == 0) continue;
                for(int j=1;j<=N;j++){
                    p.x[i][j] += x[i][k] * r.x[k][j] % MOD;
                    p.x[i][j] %= MOD;
                }
            }
        }
        return p;
    }
    matrix_t power(LL p){
        matrix_t r(1),a = *this;
        for(;p;p>>=1){
            if(p & 1) r = r * a;
            a = a * a;
        }
        return r;
    }
};
LL solve(int n,LL m){
    f[0][0] = 1;
    for(int i=1;i<=n;i++){
        f[i][0] = 1;
        for(int j=1;j<=20;j++)
            f[i][j] = (0LL + f[i-1][j] + f[i/2][j-1]) % MOD;
    }
    for(int i=1;i<=20;i++)
        len[i] = (0LL + f[n][i] - f[n/2][i] + MOD) % MOD;
    matrix_t mat(0);
    for(int i=1;i<=20;i++)
        mat.x[1][i] = len[i];
    for(int i=2;i<=21;i++)
        mat.x[i][i-1] = 1;
    g[0] = 1;
    for(int i=1;i<N;i++)
        for(int j=1;j<=20 && j <= i;j++)
            g[i] = (0LL + g[i] + 1LL * g[i-j] * len[j] % MOD) % MOD;    
    if(m < N) return g[m];
    mat = mat.power(m - 21);
    LL ans = 0;
    for(int i=1;i<N;i++)
        ans = (0LL + ans + mat.x[1][i] * g[N - i] % MOD) % MOD;
    return ans;
}
int main(){
    int n;
    LL m;
    cin >> n >> m;
    cout << solve(n,m) <<endl;
    return 0;
}
View Code

 

 

51nod 1251

枚举出现最大频率的那个频率,对于剩下的容斥就可以,发现对于枚举i,后面的复杂度是n/i

所以n / 1 + n / 2 + n / 3 + ... = O(nlogn)

                                            
  //File Name: nod1251.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月08日 星期六 14时39分39秒
                                   
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace以上是关于51nod 最近刷题  简要题解的主要内容,如果未能解决你的问题,请参考以下文章

51nod-1346: 递归

51nod-1232: 完美数

51nod-1201: 整数划分

51nod-1363: 最小公倍数之和

51nod-1296: 有限制的排列

51Nod 1571 最近等对(线段树离线查询)