Nowcoder寒假算法基础集训营1

 咕咕了好久,AC怪终于想起来WA补题了,log2r终于开始尝试写博客啦QAQ
 (以下题目顺序为自己做题时体感难度排序 )

F-对答案一时爽

Description

n n n道单选题,每题正确得 1 分,错误得0分。
 已知两人每题的选项。求两人可能的最大得分和、最小得分和。

Solution

 签到,最小得分为0,最大得分在所有题目两人中至少一人答案正确时取到。

#include
#include
#include
#include
#include
#include
#define MAXN 100+5
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
char S[MAXN],T[MAXN];
int n,ans=0;
int main(){
     
    n=Fread();
    for(int i=1;i<=n;++i)   scanf(" %c",&S[i]);
    for(int i=1;i<=n;++i){
     
        scanf(" %c",&T[i]);
        ans+=(S[i]==T[i])?2:1;
    }
    printf("%d 0",ans);
    return 0;
}

E-三棱锥之刻

Description

 求一个球心在棱长为 a a a的正三棱锥中心,半径为 r r r的球与正三棱锥截交面的面积。

Solution

 高中立体几何,所形成的四个截交面可能为空、圆、圆和正三角形截交、正三角形,分类讨论即可。

#include
#include
#include
#include
#include
#include
#define pi acos(-1.0)
using namespace std;
int main(){
     
    double a,r;
    scanf("%lf%lf",&a,&r);
    double dis1=sqrt(6.0)*a/12.0,dis2=sqrt(2.0)*a/4.0,dis3=sqrt(6.0)*a/4.0,ans=0.0;
    if(r<dis1){
     
        printf("0.00000");
        return 0;
    }
    if(r<dis2)   ans=pi*(r*r-dis1*dis1);
    else if(r<dis3){
     
        double r_=sqrt(r*r-dis1*dis1),dis=sqrt(3.0)*a/6.0,cur=3.0*(pi/3.0-acos(dis/r_))*r_*r_;
        ans=cur+3.0*dis*sqrt(r_*r_-dis*dis);
    }
    else ans=sqrt(3.0)*a*a/4.0;
    ans*=4.0;
    printf("%.5lf",ans);
    return 0;
}

B-括号

Description

 构造非空括号字符串,恰好包含 k k k个不同合法括号对。
 字符串长度 0 < l ≤ 1 e 5 , 0 ≤ k ≤ 1 e 9 00<l1e5,0k1e9

Solution

1、 依次由 L L L个"(“和 R R R个”)“组成的字符串构成的不同合法括号对数目为 L × R L×R L×R
2、 在上述序列中前 L L L个位置中第 m m m个位置插入一个”)",构成的不同合法括号对数目为 L × R + m L×R+m L×R+m

 因此,只需找到 k = L × R + m ( m ≤ L , L + R + 1 ≤ 1 e 5 ) k=L×R+m(m≤L,L+R+1≤1e5) k=L×R+m(mL,L+R+11e5)即可。
为使 L + R L+R L+R尽可能小,且 m ≤ L m≤L mL,可取:
  

 最后特判一下 k = 0 k=0 k=0的情况。

#include
#include
#include
#include
#include
#include
#define MAXN 100+5
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
int main(){
     
    int k=Fread();
    if(!k){
     
        printf("(");
        return 0;
    }
    int l=sqrt(k),r=k/l,mod=k-l*r;
    for(int i=1;i<=mod;++i) printf("(");
    printf(")");
    for(int i=mod+1;i<=l;++i)   printf("(");
    for(int i=1;i<=r;++i)   printf(")");
    return 0;
}

I-限制不互素对的排列

Description

 构造 1 ~ n 1~n 1n的一种排列,使得恰好 k k k对相邻两数 g c d gcd gcd大于1。
2 ≤ n ≤ 1 e 5 , 0 ≤ k ≤ n / 2 2≤n≤1e5,0≤k≤n/2 2n1e50kn/2

Solution
Nowcoder寒假算法基础集训营1_第1张图片
1、 注意一下 k ≤ n / 2 k≤n/2 kn/2的条件,可以把 1 ~ n 1~n 1n拆分成相邻偶数、相邻奇数和相邻整数。
2、 ( k + 1 ) (k+1) (k+1)个相邻偶数构成k个不互素对,而相邻奇数和相邻整数之间 g c d gcd gcd均为1。因此先放置(k+1)个偶数,再按顺序放置剩余的数即可。 k < n / 2 kk<n/2时均可用这种方式构造。
3、 k = n / 2 k=n/2 k=n/2,先按照 ( k − 1 ) (k-1) (k1)的构造方式,然后取出排列中的6和3依次放到相邻偶数排列后,不互素对增加了1,构造完成。
4、 特判 n < 6 n<6 n<6的情况即可。

#include
#include
#include
#include
#include
#include
#define MAXN 100000+5
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
int main(){
     
    int n=Fread(),k=Fread();
    if(n==2)    printf(k?"-1":"1 2");
    else if(n==3)    printf(k?"-1":"1 2 3");
    else if(n==4)    printf(k==2?"-1":k==1?"2 4 1 3":"1 2 3 4");
    else if(n==5)    printf(k==2?"-1":k==1?"2 4 1 3 5":"1 2 3 4 5");
    else if(!k){
     
        for(int i=1;i<=n;++i)   printf("%d ",i);
    }
    else if(k==(n>>1)){
     
        for(int i=1;i<=k;++i)
            if(i^3) printf("%d ",i<<1);
        printf("6 3 1 ");
        for(int i=2;i<=k-1;++i)   printf("%d ",i<<1^1);
        if((k<<1)^n)    printf("%d ",n);
    }
    else{
     
        for(int i=1;i<=k+1;++i)   printf("%d ",i<<1);
        for(int i=0;i<=k;++i)   printf("%d ",i<<1^1);
        for(int i=(((k+1)<<1)^1);i<=n;++i)    printf("%d ",i);
    }
    return 0;
}

A-串

Description

 求长度不超过 n n n,由小写字母构成,删除或不删除部分字符后可得到"us"的字符串个数。
2 ≤ n ≤ 1 e 6 2≤n≤1e6 2n1e6,答案对 1 e 9 + 7 1e9+7 1e9+7取模。

Solution

 递推, f [ i ] f[i] f[i]表示长度为 i i i且符合题意的字符串个数,考虑第 ( i + 1 ) (i+1) (i+1)位产生的方案数。

1、 当前i位符合题意时,第 ( i + 1 ) (i+1) (i+1)位随意填充,总方案数 26 × f [ i ] 26×f[i] 26×f[i]
2、 考虑前i位不合题意,但前 ( i + 1 ) (i+1) (i+1)位符合题意。当且仅当前 i i i位含有’u’但不存在子序列"us",且第 ( i + 1 ) (i+1) (i+1)位为’s’。前 i i i位含有"u"的字符串数目为 2 6 i − 2 5 i 26^i - 25^i 26i25i,前 i i i位存在子序列的字符串数目为 f [ i ] f[i] f[i]。总方案数 2 6 i − 2 5 i − f [ i ] 26^i - 25^i-f[i] 26i25if[i]

 因此,递推方程: f [ i + 1 ] = 25 × f [ i ] + 2 6 i − 2 5 i , f [ 2 ] = 1 f[i+1]=25×f[i]+26^i - 25^i,f[2]=1 f[i+1]=25×f[i]+26i25i,f[2]=1

#include
#include
#include
#include
#include
#include
#define ll long long
#define MAXN 1000000+5
#define MOD 1000000007
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
inline ll qpow(ll x,ll k){
     
    ll res=1ll;
    for(int i=k;i;i>>=1,x=x*x%MOD)
        if(i&1) res=res*x%MOD;
    return res;
}
ll f[MAXN];
int main(){
     
    int n=Fread();
    ll ans=0ll;
    f[2]=1;
    for(int i=2;i<n;++i)   f[i+1]=((f[i]*26)%MOD+qpow(26,i)%MOD-qpow(25,i)%MOD-f[i]%MOD+MOD)%MOD;
    for(int i=1;i<=n;++i)   ans=(ans+f[i])%MOD;
    printf("%lld",ans);
    return 0;
}

J-一群小青蛙呱蹦呱蹦呱

Description

 长度为 n n n的格子里放无穷多青蛙,第 i i i只青蛙路线为以1为首项, p [ i ] p[i] p[i]为公比的等比数列,其中 p [ i ] p[i] p[i]为第 i i i大的素数,求 n n n个格子中所有未被占据格子编号的 l c m lcm lcm
1 ≤ n ≤ 1.6 e 8 1≤n≤1.6e8 1n1.6e8,答案对 1 e 9 + 7 1e9+7 1e9+7取模。

Solution

 对每个未被占据的格子进行质因数分解,考虑每个质因子对答案的贡献。

 质因子对 l c m lcm lcm的贡献取决于质因子在未被占据的格子中最大次幂。

 每个未被占据的格子均有至少两个质因子,因此,对于质因子2,其最大次幂为 l o g 2 [ n 3 ] log_2^{[\frac{n}{3}]} log2[3n];对于其他质因子 p p p,其最大次幂为 l o g p [ n 2 ] log_p^{[\frac{n}{2}]} logp[2n]

 最终答案 2 l o g 2 [ n 3 ] ∏ i = 2 k | p k ≤ n p i l o g p i [ n 2 ] 2^{log_{2}^{[\frac{n}{3}]}}\prod_{i=2}^{k|p_k≤n} p_i^{log_{p_i}^{[\frac{n}{2}]}} 2log2[3n]i=2kpknpilogpi[2n]

(有被题目名称可爱到)

#include
#include
#include
#include
#include
#include
#define ll long long
#define MAXM 160000000+5
#define MAXN 10000000+5
#define MOD 1000000007
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
bool vis[MAXM];
int cnt=0;
ll prime[MAXN];
inline void phi_prime(int n){
     
    for(int i=2;i<=n;++i){
     
        if(!vis[i]) prime[++cnt]=i;
        for(int j=1;(j<=cnt && i*prime[j]<=n);++j){
     
            vis[i*prime[j]]=true;
            if(!(i%prime[j]))   break;
        }
    }
    return;
}
 
int main(){
     
    int n=Fread();
    phi_prime(n);
    if(n<=5){
     
        printf("empty");
        return 0;
    }
    ll cur=1ll,ans=1ll;
    while((cur<<1)<=n/3)    cur=(cur<<1)%MOD;
    ans=ans*cur%MOD;
    for(int i=2;(prime[i] && prime[i]<=n);++i){
     
        cur=1ll;
        while(prime[i]*cur<=(n>>1)) cur=cur*prime[i]%MOD;
        ans=ans*cur%MOD;
    }
    printf("%lld",ans);
    return 0;
}

C-红和蓝

Description

 给定一颗树,对树的节点 1 ~ n 1~n 1n染红色或蓝色,使得红色节点相邻节点有且仅有一个红色节点;蓝色节点相邻节点有且仅有一个蓝色节点。
1 ≤ n ≤ 1 e 5 1≤n≤1e5 1n1e5
Solution
1、 叶节点与其父节点为同色。
2、 叶节点的父节点与其父节点的父节点为异色。
3、 若将叶节点及其父节点删除,则其父节点的父节点成为新的叶节点,新叶节点必与新叶节点的父节点同色。

 因此,对于任意一个节点,当且仅当节点数目为奇数的子树个数 ≤ 1 ≤1 1时,该节点可以被染色。且该节点与其父节点颜色相反当且仅当该节点子树大小为奇数。

 首先第一遍dfs预处理出以每个节点为根的子树大小,顺带判断无解的情况,然后第二遍dfs从根节点进行染色。

#include
#include
#include
#include
#include
#include
#define MAXN 100000+5
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
struct Edge{
     
    int u,v,nxt;
    Edge(){
     nxt=-1;}
    Edge(int u,int v):u(u),v(v){
     }
}g[MAXN<<1];
int cnt=-1,tot[MAXN],siz[MAXN],head[MAXN],col[MAXN];
inline void add_edge(int u,int v){
     
    g[++cnt]=Edge(u,v);
    g[cnt].nxt=head[u];
    head[u]=cnt;
    return;
}
bool dfs1(int u,int fa){
     
    siz[u]=1;
    for(int i=head[u];~i;i=g[i].nxt)
        if(g[i].v!=fa){
     
            if(!dfs1(g[i].v,u)) return false;
            siz[u]+=siz[g[i].v];
            if(siz[g[i].v]&1)   ++tot[u];
        }
    return tot[u]<=1;
}
void dfs2(int u,int fa,int cur){
     
    col[u]=cur;
    for(int i=head[u];~i;i=g[i].nxt)
        if(g[i].v!=fa)  dfs2(g[i].v,u,siz[g[i].v]&1?cur:cur^1);
    return;
}
int main(){
     
    memset(head,0xff,sizeof head);
    int n=Fread();
    for(int i=1;i<n;++i){
     
        int u=Fread(),v=Fread();
        add_edge(u,v);
        add_edge(v,u);
    }
    if((n&1) || (!dfs1(1,-1))){
     
        printf("-1");
        return 0;
    }
    dfs2(1,-1,0);
    for(int i=1;i<=n;++i)   printf(col[i]?"R":"B");
    return 0;
}

D-点一成零

Description

 给定 n × n n×n n×n 0 − 1 0-1 01方阵,每次操作把一个元素均为"1"的连通块变为元素均为"0", k k k次询问,每次询问把方阵中的某个元素变为"1",求每次询问后把方阵变为0方阵的方案数。
n ≤ 500 , k ≤ 1 e 5 n≤500,k≤1e5 n500k1e5,强制在线。

Solution

 并查集维护每个连通块大小。设连通块数目为 N N N,第 i i i个连通块大小为 s i z e i size_i sizei,则方案数为 N ! ∏ i = 1 N s i z e i N!\prod_{i=1}^{N} size_i N!i=1Nsizei,每次查询时更新连通块大小及数目。

#include
#include
#include
#include
#include
#include
#define ll long long
#define MAXN (500+5)
#define MOD 1000000007ll
using namespace std;
inline int Fread(){
     
    int val=0;
    bool sign=false;
    char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && (ch^'-'));
    val=(sign=!(ch^'-'))?0:ch^48;
    while(~(ch=getchar()) && (ch>='0' && ch<='9'))  val=(val<<1)+(val<<3)+(ch^48);
    return sign?~val+1:val;
}
int dx[5]={
     0,1,-1,0,0};
int dy[5]={
     0,0,0,1,-1};
int n,k,cnt,fa[MAXN*MAXN];
ll tot=1ll,fac[MAXN*MAXN],siz[MAXN*MAXN];
char s[MAXN][MAXN];
inline ll qpow(ll x,ll k){
     
    ll res=1ll;
    for(ll i=k;i;i>>=1,x=x*x%MOD)
        if(i&1) res=res*x%MOD;
    return res;
}
inline ll inv(ll x){
     
    return qpow(x,MOD-2);
}
inline int Find(int x){
     
    return x==fa[x]?x:fa[x]=Find(fa[x]);
}
inline void Union(int x,int y){
     
    int rx=Find(x),ry=Find(y);
    if(rx==ry)  return;
    tot=(tot*inv(siz[rx]))%MOD;
    tot=(tot*inv(siz[ry]))%MOD;
    fa[ry]=rx;
    siz[rx]+=siz[ry];
    tot=(tot*siz[rx])%MOD;
    --cnt;
    return;
}
inline bool judge(int x,int y){
     
    return (~x) && (~y) && (x<n) && (y<n);
}
int main(){
     
    n=Fread();
    siz[0]=fac[0]=1ll;
    for(int i=1;i<=n*n;++i){
     
        fa[i]=i;
        siz[i]=1ll;
        fac[i]=fac[i-1]*i%MOD;
    }
    for(int i=0;i<n;++i){
     
        scanf("%s",s[i]);
        for(int j=0;j<n;++j)
        if(!(s[i][j]^'1')){
     
            ++cnt;
            for(int k=1;k<=4;++k){
     
                int x=i+dx[k],y=j+dy[k];
                if(!judge(x,y)) continue;
                if(!(s[x][y]^'1'))    Union(i*n+j,x*n+y);
            }
        }
    }
    k=Fread();
    while(k--){
     
        int x=Fread(),y=Fread();
        if(s[x][y]^'1'){
     
            s[x][y]='1';
            ++cnt;
            for(int i=1;i<=4;++i){
     
                int x_=x+dx[i],y_=y+dy[i];
                if(!judge(x_,y_)) continue;
                if(!(s[x_][y_]^'1'))    Union(x*n+y,x_*n+y_);
            }
        }
        printf("%lld\n",fac[cnt]*tot%MOD);
    }
    return 0;
}

 还剩下两道题,一道不会的数学和一道大模拟。好像挺心把的(但不影响2048好玩hhh),应该有时间也不会去写了,以上。
Nowcoder寒假算法基础集训营1_第2张图片

你可能感兴趣的:(笔记,算法)