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; }
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; }
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; }