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}\))的积性函数\(\tau\)(n)暴力枚举出结果判断即可

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

\[\tau(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;
}
posted @ 2020-02-05 22:23  BT-7274  阅读(327)  评论(0编辑  收藏  举报