zoukankan      html  css  js  c++  java
  • 2020 省选模拟测试 Round #6 solution (20/02/05)

    【比赛链接】http://59.61.75.5:8018/contest/216

    A. 旅行

    【题意】有一棵 $n$ 个节点的树,给定其中 $K$ 个节点,对所有的 $i$,求从节点 $i$ 出发,经过这 $K$ 个节点的最短路程。

    【数据范围】$Kle nle 5 imes 10^5,1le wle 10^6$。

    【题解】

    不难发现,答案即为 $K$ 个点和点 $i$ 所构成的虚树路径和的两倍减去 $i$ 到 $K$ 个点的最大距离。

    两遍 $dfs$ 转移树形 $dp$ 即可。

    效率 $O(n)$。期望得分:100。

    【代码】

     1 #include<bits/stdc++.h>
     2 const int inf=1<<30;
     3 const int maxn=500000+10;
     4 struct edge { int v,nxt,w; } e[maxn<<1];
     5 int siz[maxn],h[maxn],cnt,n,k;
     6 bool flag[maxn];
     7 long long f[maxn],g[maxn],max1[maxn],max2[maxn],max[maxn];
     8 inline void dfs ( int u,int fr )
     9 {
    10     siz[u]=flag[u];max1[u]=flag[u]?0:-inf;max2[u]=-inf;
    11     for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr )
    12     {
    13         dfs(e[i].v,u);siz[u]+=siz[e[i].v];
    14         if ( siz[e[i].v] ) f[u]+=f[e[i].v]+e[i].w;
    15         if ( max1[e[i].v]+e[i].w>max1[u] ) max2[u]=max1[u],max1[u]=max1[e[i].v]+e[i].w;
    16         else if ( max1[e[i].v]+e[i].w>max2[u] ) max2[u]=max1[e[i].v]+e[i].w;
    17     }
    18 }
    19 inline void DFS ( int u,int fr )
    20 {
    21     for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr )
    22     {
    23         g[e[i].v]=f[u]+g[u]+e[i].w;
    24         if ( siz[e[i].v] ) g[e[i].v]-=f[e[i].v]+e[i].w;
    25         if ( k-siz[e[i].v] )
    26         {
    27             max[e[i].v]=max[u]+e[i].w;
    28             if ( max1[e[i].v]+e[i].w!=max1[u] ) max[e[i].v]=std::max(max[e[i].v],max1[u]+e[i].w);
    29             else max[e[i].v]=std::max(max[e[i].v],max2[u]+e[i].w);
    30         }
    31         DFS(e[i].v,u);
    32     }
    33 }
    34 signed main()
    35 {
    36     scanf("%d%d",&n,&k);
    37     for ( int i=1,u,v,w;i<n;i++ )
    38         scanf("%d%d%d",&u,&v,&w),
    39         e[++cnt].nxt=h[u],e[h[u]=cnt].v=v,e[cnt].w=w,
    40         e[++cnt].nxt=h[v],e[h[v]=cnt].v=u,e[cnt].w=w;
    41     for ( int i=1;i<=n;i++ ) max[i]=-inf;
    42     for ( int i=1,u;i<=k;i++ ) scanf("%d",&u),flag[u]=true,max[u]=0;
    43     dfs(1,0);DFS(1,0);
    44     for ( int i=1;i<=n;i++ ) printf("%lld
    ",(f[i]+g[i])*2-std::max(std::max(max1[i],max2[i]),max[i]));
    45     return 0;
    46 }
    DTOJ4703

    B. 求和

    【题意】令 $f(n)=sum_{i=1}^{n} sum_{j=1}^{n} g c d(i, j, n)$,求 $sum_{i=1}^n f(i) mod P$。

    【数据范围】$1le nle 10^9, Ple 10^9+9$,$P$ 是质数。

    【题解】

    $f(i)=sumlimits_{d|i}sumlimits_{j=1}^{frac{i}{d}}sumlimits_{k=1}^{frac{i}{d}}dsumlimits_{e|gcd(frac{i}{d},j,k)}mu(e) xlongequal[quad]{T=de}sumlimits_{T|i}sumlimits_{d|T}dmu(frac{T}{d})(frac{i}{T})^2=sumlimits_{T|i}varphi(T)(frac{i}{T})^2=sumlimits_{T|i}varphi(frac{i}{T})T^2$

    $sumlimits_{i=1}^{n}f(i)=sumlimits_{i=1}^nsumlimits_{T|i}varphi(frac{i}{T})T^2=sumlimits_{T=1}^n T^2sumlimits_{i=1}^{lfloorfrac{n}{T} floor}varphi(i)$

    直接整除分块+杜教筛求 $phi$ 的前缀和即可。

    期望得分:100。

    【代码】

     1 #include<bits/stdc++.h>
     2 const int maxn=5000000+10;
     3 int n,m=5000000,mod,inv6,inv2;
     4 inline int power ( int x,int y )
     5 {
     6     int z=1;
     7     for ( ;y;y>>=1,x=1LL*x*x%mod ) if ( y&1 ) z=1LL*z*x%mod;
     8     return z;
     9 }
    10 inline int sum ( int x ) { return 1LL*x*(x+1)%mod*(2*x+1)%mod*inv6%mod; }
    11 long long mu[maxn],phi[maxn];
    12 int pr[maxn],tot;
    13 bool flag[maxn];
    14 std::unordered_map<int,long long> map;
    15 inline long long calc ( int n )
    16 {
    17     if ( n<=m ) return phi[n];
    18     if ( map.count(n) ) return map[n];
    19     long long res=0;
    20     for ( int i=2,j;i<=n;i=j+1 ) j=n/(n/i),res=(res+(j-i+1)%mod*calc(n/i))%mod;
    21     res=(1LL*n*(n+1)%mod*inv2%mod-res+mod)%mod;
    22     return map[n]=res;
    23 }
    24 signed main()
    25 {
    26     scanf("%d%d",&n,&mod);inv6=power(6,mod-2);inv2=power(2,mod-2);
    27     mu[1]=phi[1]=1;
    28     for ( int i=2;i<=m;i++ )
    29     {
    30         if ( !flag[i] ) pr[++tot]=i,mu[i]=-1,phi[i]=i-1;
    31         for ( int j=1;j<=tot and pr[j]*i<=m;j++ )
    32         {
    33             flag[pr[j]*i]=true;
    34             if ( !(i%pr[j]) ) { mu[pr[j]*i]=0;phi[pr[j]*i]=phi[i]*pr[j];break; }
    35             mu[pr[j]*i]=-mu[i];phi[pr[j]*i]=phi[i]*(pr[j]-1);
    36         }
    37     }
    38     for ( int i=2;i<=m;i++ ) phi[i]=(phi[i-1]+phi[i])%mod;
    39     int ans=0;
    40     for ( int i=1,j;i<=n;i=j+1 ) j=n/(n/i),ans=(ans+1LL*sum(n/i)*(calc(j)-calc(i-1)+mod))%mod;
    41     return !printf("%d
    ",ans);
    42 }
    DTOJ4704

    C. 递增

    【题意】已知 $l_{1dots n}$ 和 $r_{1dots n}$,求所有满足以下条件的序列 ${a_n}$ 的元素和的和:对任意 $iin[1,n]$ 满足 $l_ile a_ile r_i$​​,且 ${a_n}$ 是非严格递增的。

    【数据范围】$2le nle 50, 0le l_ile r_i< 2^{60}$。

    【题解】

    考虑先将 $l,r$ 离散化。为了处理方便离散化所有的 $l_i$ 和 $r_i+1$。记离散化后的权值数组为 $val_i$,对应区间 $[val_i,val_{i+1}-1]$。

    显然原来的每段可以拆分成若干个小区间。根据题目要求,$a_n$ 所在的小区间编号单调不降。

    考虑 $dp$,记 $f_{i,j}$ 表示前 $i$ 个元素,第 $i$ 个元素在第 $j$ 个小区间内的元素和。显然可以枚举 $k,l$,从 $f_{k,l}$ 转移。

    考虑转移。根据转移,$[k+1,i]$ 中的元素都必须在区间 $[val_j,val_{j+1}-1]$ 内。记元素的取值范围区间为 $[L,R]$,共需要取 $t=i-k$ 个元素。

    显然转移的方案数为 $cnt=C_{R-L+t}^t$,根据数列相关知识,所有转移方案的权值总和为 $sum=t imes frac{L+R}{2} imes cnt$。

    发现无法直接转移,因此需记录 $g_{i,j}$ 表示前 $i$ 个元素,第 $i$ 个元素在第 $j$ 个小区间内的方案数。

    则有:$$f_{i,j}=sum cnt imes f_{k,l}+sum*g_{k,l},g_{i,j}=sum cnt imes g_{k,l}$$。直接转移即可。

    效率 $O(n^4)$。期望得分:100。

    【代码】

     1 #include<bits/stdc++.h>
     2 const long long mod=998244353,inv2=(mod+1)>>1;
     3 std::set<long long> s;
     4 std::unordered_map<long long,int> map;
     5 int n,tot,L[500],R[500];
     6 long long l[500],r[500],val[500],f[500][500],g[500][500],ans,inv[500];
     7 inline long long C ( long long x,int y )
     8 {
     9     long long res=inv[y];
    10     for ( int i=1;i<=y;i++ ) (res*=(x-i+1)%mod)%=mod;
    11     return res;
    12 }
    13 signed main()
    14 {
    15     scanf("%d",&n);inv[0]=inv[1]=1;
    16     for ( int i=1;i<=n;i++ ) scanf("%lld",&l[i]),s.insert(l[i]);
    17     for ( int i=1;i<=n;i++ ) scanf("%lld",&r[i]),s.insert(r[i]+1);
    18     for ( int i=2;i<=n;i++ ) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    19     for ( int i=2;i<=n;i++ ) (inv[i]*=inv[i-1])%=mod;
    20     for ( long long x:s ) val[map[x]=++tot]=x;
    21     for ( int i=1;i<=n;i++ ) L[i]=map[l[i]],R[i]=map[r[i]+1]-1;
    22     g[0][0]=1;
    23     for ( int i=1;i<=n;i++ ) for ( int j=L[i];j<=R[i];j++ )
    24     {
    25         for ( int k=0;k<i;k++ )
    26         {
    27             bool flag=true;
    28             for ( int p=k+1;p<=i;p++ ) flag&=(L[p]<=j and j<=R[p]);
    29             if ( !flag ) continue;
    30             int t=i-k;
    31             long long pL=val[j],pR=val[j+1]-1,cnt=C(pR-pL+t,t);
    32             long long res=(pL+pR)%mod*t%mod*inv2%mod*cnt%mod;
    33             for ( int l=0;l<j;l++ ) (f[i][j]+=cnt*f[k][l]+g[k][l]*res)%=mod,(g[i][j]+=cnt*g[k][l])%=mod;
    34         }
    35     }
    36     for ( int i=L[n];i<=R[n];i++ ) (ans+=f[n][i])%=mod;
    37     return !printf("%lld
    ",ans);
    38 }
    DTOJ4705
  • 相关阅读:
    jQuery中jsonp的跨域处理,no access-control-allow-origin,unexpected token
    doT中嵌套for循环的使用
    c++ new带括号和不带括号
    python装饰器之使用情景分析
    Python中classmethod与staticmethod区别
    python作用域 scope
    duck type鸭子类型
    EAFP和LBYL 两种防御性编程风格
    c++重载、覆盖和隐藏
    c++ 名字粉碎(name mangling)
  • 原文地址:https://www.cnblogs.com/RenSheYu/p/12266604.html
Copyright © 2011-2022 走看看