• 2020牛客寒假算法基础训练营1


    2020牛客寒假算法基础训练营1

    A.honoka和格点三角形(计数)

    传送门

    题意:给定n*m个格点构造出的矩阵,问最多可以构造出多少个面积为1的三角形

    题解:直接计数,讨论所有情况即可,注意乘法取模

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    ll mod = 1e9+7;
    int main()
    {
        ll n,m;
        cin>>n>>m;
        ll s=(n-2)*(m-1)*4%mod+(n-1)*(m-2)*4%mod;
        s=(s+2*(n-1)*(m-2)%mod*(m-2)%mod+2*(m-1)*(n-2)%mod*(n-2)%mod)%mod;
        s=(s+2*(n-2)*(m-1)%mod*(m-2)%mod+2*(m-2)*(n-1)%mod*(n-2)%mod)%mod;
        cout<<s<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    B.kotori和bangdream(期望)

    传送门

    题意:每次有x%的概率获得a分,剩下的概率获得b分,一共进行n次,求得分期望

    题解:结果是ans = (x%a+(1-(x%))b)*n

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    #include<iomanip>
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    
    int main()
    {
        double n,x,a,b;
        cin>>n>>x>>a>>b;
        x/=100;
        double ans = n*(x*a+(1-x)*b);
        cout<<fixed<<setprecision(2);
        cout<<ans<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    C.umi和弓道(双指针)

    传送门

    题意:给定一个起始坐标和一群坐标,这些坐标一定不在坐标轴上,现在需要在x轴或y轴设立一个挡板,这个挡板会遮蔽其他坐标和起始坐标的连线,要求遮蔽后最多只有k个点可以连通起始坐标,求这个挡板最短的长度

    题解:分两种情况讨论,挡板在x轴上或在y轴上,将其他坐标与起始坐标的连线在x轴和y轴的交点分别记录下来,维护一个挡板长度的最小值,用双指针扫描

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    #include<iomanip>
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    
    int main()
    {
        double x0,y0;
        int n,k;
        cin>>x0>>y0>>n>>k;
        k = n - k;
        vector<double> x,y;
        for(int i = 0; i < n; i++){
            double x1,y1;
            cin>>x1>>y1;
            if(x1*x0<0){
                y.push_back(y0-x0*(y1-y0)/(x1-x0));
            }
            if(y1*y0<0){
                x.push_back(x0-y0*(x1-x0)/(y1-y0));
            }
        }
        sort(x.begin(),x.end());
        sort(y.begin(),y.end());
        double ans = 1e18;
        if(x.size()>=k){
            int l = 0,r = k-1;
            while(r<x.size()){
                ans = min(ans,x[r]-x[l]);
                r++;l++;
            }
        }
        if(y.size()>=k){
            int l = 0,r = k-1;
            while(r<y.size()){
                ans = min(ans,y[r]-y[l]);
                r++;l++;
            }
        }
        cout<<fixed<<setprecision(8);
        if(ans==1e18){
            cout<<-1<<endl;
        }else
            cout<<ans<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    D.hanayo和米饭(暴力)

    传送门

    题意:1,2,3...n这n个数会给你n-1个,让你把那个没给你的数输出

    题解:直接暴力

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    bool vis[100005];
    int main()
    {
        int n,x;
        cin>>n;
        for(int i = 0; i < n-1; i++)
        {
            cin>>x;
            vis[x] = true;
        }
        for(int i = 1; i <= n; i++)
        {
            if(!vis[i]){
                cout<<i<<endl;
                break;
            }
        }
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    E.rin和快速迭代(数论)

    传送门

    题意:给定一个数x,函数f(x)的值为x的因子个数,若当前的f(x)不为2,则继续进行f(f(x)),直到f(x)为2,求此时的计算次数

    题解:本质上就是利用O((sqrt{n}))的积性函数( au)(n)暴力枚举出结果判断即可

    ( au)(n)函数表示的是正整数n的所有正因子个数,设n的质因子分解为n=(p_1^{a_1})*(p_2^{a_2})......(p_s^{a_s}),那么可以得出

    [ au(n) = (a_1+1)*(a_2+1)*...*(a_s+1) = prod_{j=1}^s(a_j+1) ]

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    ll count(ll n)
    {
        ll s = 1;
        for(ll i = 2;i*i<=n;i++){
            if(n%i==0){
                ll a = 0;
                do{
                    n/=i;
                    a++;
                }while(n%i==0);
                    s = s*(a+1);
            }
        }
        if(n>1)s*=2;
        return s;
    }
    int main()
    {
        ll n;
        ll ans = 0;
        cin>>n;
        while(1){
            ans++;
            if(count(n)==2)break;
            n = count(n);
        }
        cout<<ans<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    F.maki和tree(并查集)

    传送门

    题意:给定一个n个点n-1条边的树,每个点为“W”白色或“B”黑色,问可以构成多少条两点之间只有一个黑色点的简单路径。、

    题解:经过一个黑点的简单路径有两种,一种是路径两端都是白点,一种是有一端是黑点,我们统计每个白色连通块的权值,然后通过统计每个黑点相邻白点的权值和求出第二种的路径,对于第一种的路径,假设1个黑点有k个相邻白点,第一种路径就是从第1个白点开始,剩余的白点权值总和与当前白点的乘积,即

    (sum_{i=1}^{k}sum_{j=i+1}^kf(i)*f(j))

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 111111;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    int f[N];
    ll knum[N];
    ll st[N];
    vector<int> g[N];
    ll n;
    string s;
    int find(int x)
    {
        if(f[x]==x)return x;
        else
            return find(f[x]);
    }
    void uni(int a,int b)
    {
        int p = find(a);int q = find(b);
        if(p!=q){
            if(knum[p]>knum[q]){
                f[q] = p;
                knum[p]+=knum[q]+1;
            }else
            {
                f[p] = q;
                knum[q]+=knum[p]+1;
            }
        }
    }
    ll get(vector<int>temp)
    {
        ll len = temp.size();
        ll res = 0;
        if(len==0)return 0;
        vector<int> pre(len+10,0);
        pre[0] = temp.at(0);
        for(int i = 0; i < len; i++)//统计出以黑点为一端的种数
            res+=temp[i];
        for(int i = 1; i < len; i++)//前缀和维护每个点的权值合集
            pre[i] += pre[i-1]+temp[i];
        for(int i = 1; i < len; i++)//计算
            res+=temp[i]*pre[i-1];
        return res;
    }
    int main()
    {
        cin>>n>>s;
        for(int i = 1;i <= n; i++)f[i] = i;
        for(int i = 1;i < n; i++){
            int x,y;
            cin>>x>>y;
            g[x].push_back(y);
            g[y].push_back(x);
            if(s[x-1]=='W'&&s[y-1]=='W')uni(x,y);
        }
        ll ans = 0;
        for(int i = 1; i <= n; i++)st[i] = knum[find(i)]+1;
        for(int i = 1; i <= n; i++)
        {
            if(s[i-1]=='B'){
                vector<int> temp;
                for(int j = 0; j < g[i].size(); j++){
                    if(s[g[i][j]-1]=='W')temp.push_back(st[g[i][j]]);
                }
                ans+=get(temp);
            }
        }
        cout<<ans<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    G.eli和字符串(字符串,双指针)

    传送门

    题意:给定一个字符串,要求截取一个最短的子串,这个子串至少包含k个相同的字符

    题解:利用map存放每一个字符在字符串中的下标,当前字符数量满足k时调出区间长度维护最小值即可

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    
    int main()
    {
        int k,n;
        cin>>n>>k;
        map<char,vector<int>> mp;
        string s;
        int ans = 2000000;
        cin>>s;
        for(int i = 0; i < s.size(); i++)
        {
            mp[s[i]].push_back(i);
            if(mp[s[i]].size()>=k){
                int l = mp[s[i]][mp[s[i]].size()-k];
                int len = i-l+1;
                ans = min(ans,len);
            }
        }
        if(ans==2000000)
            cout<<-1<<endl;
        else
            cout<<ans<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    H.nozomi和字符串 (字符串,尺取法)

    传送门

    题意:给定一个01串,最多可以修改k次,仅限将0改为1,1改为0,问这个01串中最长连续子串是多长

    题解:尺取维护k次修改区间

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    string s;
    int n,k;
    int deal(char x)
    {
        int L=0,R=0,change=0,ans=1;
        for (int i = 0; i < n; i++)
        {
            if(s[i]==x)
            {
                if(change<k)
                {
                    change++;
                    R++;
                }
                else
                {
                    while(L<=R&&s[L]!=x)L++;
                    L++;
                    R++;
                }
            }
            else R++;
            ans = max(ans,R-L);
        }
        return ans;
    }
    int main()
    {
        cin>>n>>k>>s;
        cout<<max(deal('0'),deal('1'))<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    I.nico和niconiconi(线性DP)

    传送门

    题意:给定长度为n的字符串,子串nico权值为a,子串niconi权值为b,子串niconiconi权值为c,求这个字符串的最大权值,子串不可重复使用

    题解:dp即可,计dp[i]表示前i个字符的最大权值,那么有转移方程

    if(substring(i-3,i))==nico,dp[i] = max(dp[i-4]+a.dp[i])

    if(substring(i-5,i))==nico,dp[i] = max(dp[i-6]+b.dp[i])

    if(substring(i-9,i))==nico,dp[i] = max(dp[i-10]+c.dp[i])

    最后输出dp[n]即可

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    
    int main()
    {
        vector<ll> dp(300005,0);
        int n,a,b,c;
        string s;
        cin>>n>>a>>b>>c;
        cin>>s;
        for(int i = 0; i < s.size(); i++){
            if(i>0)
                dp[i] = dp[i-1];
            if(i>=3&&s.substr(i-3,4)=="nico"){
                dp[i] = max(dp[i-3]+a,dp[i]);
            }
            if(i>=5&&s.substr(i-5,6)=="niconi"){
                dp[i] = max(dp[i-5]+b,dp[i]);
            }
            if(i>=9&&s.substr(i-9,10)=="niconiconi"){
                dp[i] = max(dp[i-9]+c,dp[i]);
            }
        }
        cout<<dp[n-1]<<endl;
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    

    J.u's的影响力(矩阵快速幂,费马小定理)

    传送门

    题意:给定n,x,y,a,b,设f(1) = x,f(2) = y,之后为f(i) = f(i-1) * f(i-2) *(a^b),让你求f(n)的值,结果%1e9+7。

    题解:通过观察你会发现表达式由x,y,a三个因子组成,且x和y的幂数都构成斐波那契数列,而a的幂数则是斐波纳契数列的变种,这时候很容易就想到利用矩阵快速幂去解决这个问题。利用费马小定理进行降幂处理,因为%的1e9+7是一个质数,所以对于不等于1e9+7的数字a来说,(a^{1e9+6} equiv (1 mod 1e9+7)),这样可以对x,y,a的系数进行降幂,而它们的系数则有矩阵快速幂递推公式求出。复杂度为O(logn)

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<string>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    #include<vector>
    
    using namespace std;
    using ll = long long;
    const ll N = 1e6;
    const double PI = acos(-1.0);
    #define Test ll tesnum;tesnum = read();while(tesnum--)
    ll read();
    const int MOD = 1e9+7;
    struct Mx{
        ll mx[2][2];
        void init(){
            for(int i = 0; i < 2; i++){
                for(int j = 0; j < 2; j++){
                    mx[i][j] = (i==j?1:0);
                }
            }
        }
        void clear(){
            for(int i = 0; i < 2; i++){
                for(int j = 0; j < 2; j++){
                    mx[i][j] = 0;
                }
            }
        }
    };
    Mx Mxmulti(Mx a, Mx b,int mod)
    {
        Mx res;
        res.clear();
        for(int i = 0; i < 2; i++){
            for(int j = 0; j < 2; j++){
                for(int k = 0; k < 2; k++){
                    res.mx[i][j]+=a.mx[i][k]*b.mx[k][j]%mod;
                    res.mx[i][j]%=mod;
                }
            }
        }
        return res;
    }
    Mx Mxpow(Mx a,ll b,int mod)
    {
        Mx ans;ans.init();
        while(b > 0){
            if(b&1)
                ans = Mxmulti(ans,a,mod);
            b>>=1;
            a = Mxmulti(a,a,mod);
        }
        return ans;
    }
    ll quick_mod(ll a, ll b, ll mod)
    {
        if(b==0)return 1;
        ll ans = 1;
        a%=mod;
        while(b>0)
        {
            if(b&1)
                ans = (ans*a)%mod;
            b>>=1;
            a = (a*a)%mod;
        }
        return ans%mod;
    }
    int main()
    {
        ll n,a,b,x,y;
        cin>>n>>x>>y>>a>>b;
        if(n==1){
            cout<<x%MOD<<endl;
        }else
            if(n==2){
                cout<<y%MOD<<endl;
            }else
                if(x%MOD==0||y%MOD==0||a%MOD==0){
                    cout<<0<<endl;
                }else{
                    Mx A;
                    A.clear();
                    A.mx[0][0] = A.mx[0][1] = A.mx[1][0] = 1;
                    A = Mxpow(A,n-2,MOD-1);
                    ll f1 = A.mx[0][0];
                    ll f2 = A.mx[0][1];
                    ll ans = 1;
                    ans = ans*quick_mod(x,f2,MOD)%MOD;
                    ans = ans*quick_mod(y,f1,MOD)%MOD;
                    ans = ans*quick_mod(quick_mod(a,b,MOD),f1+f2-1,MOD)%MOD;
                    cout<<ans<<endl;
                }
        return "BT7274", NULL;
    }
    
    inline ll read() {
        ll hcy = 0, dia = 1;char boluo = getchar();
        while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
        while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
        return hcy * dia;
    }
    
  • 相关阅读:
    sqlmap注入分类
    sqlmap简单中文说明
    【Python Learning第一篇】Linux命令学习及Vim命令的使用
    模拟退火算法从原理到实战【基础篇】
    平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小。
    使用VMWareWorkstation10搭建学习环境笔记
    洛谷P1313 计算系数【快速幂+dp】
    浅析Numpy.genfromtxt及File I/O讲解
    持续交付中高效率与高质量
    持续集成CI与自动化测试
  • 原文地址:https://www.cnblogs.com/cloudplankroader/p/12266996.html
Copyright © 2020-2023  润新知