HGOI 20190708 题解

Problem A 拿出勇气吧

幸运数字每一位是$4$或者$7$,现在给出一个数字每位数位上数的和为n,求出最小的幸运数n

对于100%的数据,$n\leq 10^6$

Sol : 显然本题要求数的长度尽可能短,于是显然是先放$7$放$4$并且$7$放在较低位。

这就等价于求不定方程$4x + 7y = n $的最小整数解x,然后只要延续输出x个4,y个7即可。

只需要做一次exgcd就可以求出。

复杂度应该是答案的长度 $O(length)$

# include <bits/stdc++.h>
# define int long long
using namespace std;
struct node {
    int x,y; bool flag;
};
int exgcd(int a,int b,int &x,int &y)
{
    if (b==0) {
        x=1;y=0;
        return a;
    }
    int g=exgcd(b,a%b,x,y);
    int t=x;x=y;y=t-a/b*x;
    return g;
}
node getmin(int a,int b,int c) 
{
    int x,y; int g=exgcd(a,b,x,y);
    if (c%g!=0) return (node){-1,-1,false};
    int k=c/g,ga=a/g,gb=b/g;
    x=(x*k%gb+gb)%gb;
    y=(c-a*x)/b;
    return (node){x,y,true};
}
signed main()
{
    int n;scanf("%d",&n);
    node t=getmin(4,7,n);
    for (int i=1;i<=t.x;i++) printf("4");
    for (int i=1;i<=t.y;i++) printf("7"); 
    return 0;
}
Courage.cpp

Problem B 生死之对决

幸运数字每一位是$4$或者$7$,给出$ p \in [pl,pr] \in Z,v \in [vl,vr] \in Z $ 且$p,v$ 在所属区间等概率出现。

求出区间$[min\{v,p\} , max \{v,p\}]$包含幸运数字恰好是$k$个的概率。

对于100%的数据$1 \leq ql \leq qr \leq 10^9,1 \leq vl \leq vr \leq 10^9,1 \leq k \leq 10^3 $

Sol :首先在$[1,10^9]$的幸运数字是$2^1 + 2^2 + ... + 2^9 = 1022$个,所以我们可以基于枚举每一个幸运数字对答案造成的贡献(用乘法原理)来进行计数。

设排序后的幸运数字为$p_i (1\leq i \leq 1022)$ 那么我们枚举第$p_i $到$p_{i+k-1}$这$k$个幸运数字在$[min\{v,p\} , max\{v,p\}]$区间内。

所以有$min\{p,v\} \in [p_{i-1} +1 , p_i] ,max\{p,v\} \in [p_{i+k-1},p_{i+k}-1]$ 

为了拆掉$min,max$所以我们要讨论$p,v$的大小关系。

当$p \leq v$ 时显然有 $p \in  [p_{i-1} +1 , p_i]  , v \in  [p_{i+k-1},p_{i+k}-1] $ 

当$p \geq v$ 时显然有 $v \in [p_{i-1} +1 , p_i] , p \in [p_{i+k-1},p_{i+k}-1]  $

注意到上述我们在$p = v $的时候可能会重复计算,而且当且仅当$ k = 1$的时候出现,所以需要特判。

# pragma GCC optimize(3) 
# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e3+100;
int p[N];
int n;
void getlucky(int num,int lim)
{
    if (num>lim) return;
    if (num!=0) p[++n]=num;
    getlucky(num*10+4,lim);
    getlucky(num*10+7,lim);
}
signed main()
{
    getlucky(0,2e9); int pl,pr,vl,vr,k; 
    scanf("%lld%lld%lld%lld%lld",&pl,&pr,&vl,&vr,&k);
    sort(p+1,p+1+n);
    p[n+1]=2e9+10; int ans=0;
    for (int i=1;i<=n;i++) {
        if (min(p[i],pr)-max(p[i-1]+1,pl)+1>=0&&min(p[i+k]-1,vr)-max(p[i+k-1],vl)+1>=0) {
            ans+=(min(p[i],pr)-max(p[i-1]+1,pl)+1)*(min(p[i+k]-1,vr)-max(p[i+k-1],vl)+1);
        }
        if (min(p[i+k]-1,pr)-max(p[i+k-1],pl)+1>=0&&min(p[i],vr)-max(p[i-1]+1,vl)+1>=0) {
            ans+=(min(p[i+k]-1,pr)-max(p[i+k-1],pl)+1)*(min(p[i],vr)-max(p[i-1]+1,vl)+1);
        }
    }
    if (k==1) {
        for (int i=1;i<=n;i++)
         if (p[i]>=vl&&p[i]<=vr&&p[i]>=pl&&p[i]<=pr) ans--;
    } 
    printf("%.12lf\n",(double)ans/(double)(vr-vl+1)/(double)(pr-pl+1));
    return 0;
}
Match.cpp

Problem C 最后的结局

一棵树上的点和父亲之间可能是存在魔法边和普通边,定义合法三元组$(i,j,k)$表示从$i$出发到$j$路径上和$i$出发到$k$路径上都至少存在一条魔法边。

注意到不同的$(i,j,k)$都算不同的合法三元组。计算这棵树中合法三元组的数目。

对于100%的数据 $ 1 \leq n \leq 10^5 $

Sol : 直接树形DP即可,设$f_u$ 表示节点 $u$ 跑到所有节点的路径中含有魔法边的节点个数。

       第一次跑dfs从儿子更新到父亲,转移方程是 $f_{u}= \sum\limits_{v \in u_{son}} \left\{\begin{matrix} size_{v}&(u,v) \ is \ magic \ edge  \\  f_{v}&  (u,v) \ isn't \ magic \ edge  \end{matrix}\right.$

      第二次跑dfs从父亲更新到儿子,转移方程是$f_v = \left\{\begin{matrix} f_u & (u,v)\  isn't \ magic \ edge\\  n-size_v & (u,v) \ is \ magic \ edge \end{matrix}\right.$ 

  复杂度$O(n)$

# pragma GCC optimize(3) 
# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e5+10;
struct rec{
    int pre,to,w;
}a[N<<1];
int f[N],tot,head[N],size[N],n;
bool check(int x) {
    while (x) {
        if (!(x%10==4||x%10==7)) return false;
        x/=10;
    }
    return true;
}
void adde(int u,int v,int w)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    a[tot].w=w;
    head[u]=tot;
}
void dfs1(int u,int fa) 
{
    size[u]=1; f[u]=0;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        dfs1(v,u);  size[u]+=size[v]; 
        if (a[i].w==1) f[u]+=size[v];
        else f[u]+=f[v];
    }
}
void dfs2(int u,int fa) 
{   
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (v==fa) continue;
        if (a[i].w==1) f[v]+=n-size[v];
        else f[v]=f[u]; 
        dfs2(v,u);
    }
} 
signed main()
{
//  freopen("ending.in","r",stdin);
//  freopen("ending.out","w",stdout);
    scanf("%lld",&n);
    for (int i=1;i<n;i++) {
        int u,v,w; scanf("%lld%lld%lld",&u,&v,&w);
        if (check(w)) adde(u,v,1),adde(v,u,1);
        else adde(u,v,0),adde(v,u,0);
    } 
    dfs1(1,0); dfs2(1,0);
    int ans=0;
    for (int i=1;i<=n;i++) ans+=(long long)f[i]*(f[i]-1);
    printf("%lld\n",ans); 
    return 0;
 }
Ending.cpp

 

posted @ 2019-07-08 15:02  ljc20020730  阅读(187)  评论(0编辑  收藏  举报