2018.08.14【2018提高组】模拟A组 比赛总结

题解

这次的A组难得得水。

T1

这题我一看,就想起了GDOI的一道题——密码锁

\(O(n)\)算法——差分

于是乎兴奋地发现这道题可以用差分来解。
\(f_i=a_i-a_{i-1}\)
然后就可以发现一些神奇的东西:

\(f_i>0\)时,第i个数比第i-1个数的操作数多了\(f_i\),可以先一起+1,再给第i个数+1
\(f_i=0\)时,第i个数与第i-1个数的操作数一样多,一起+1即可
\(f_i<0\)时,第i个数比第i-1个数的操作数少了\(-f_i\),因此只能与它一起进行\(a_i\)次操作。

于是可以用一个维护一下每一个操作(具体见标程),考场AC!

\(O(n\log_2n)\)算法——分治+RMQ

差分的方法可能比较难理解,不过这个做法比较简单。
可以发现,对于每个区间\([l,r]\),一开始我们是可以把里面所有数+1的,直到加到\(a_{min}\)(设min为l到r的最小值),接下来就是\([l,min]\)\([min+1,r]\)这两个区间+1。
因此,这题可以用分治来做。
但是暴力求最小值会TLE,因此要用RMQ优化一下。

T2

比赛时毫无头绪,于是乎打了一个暴力,水了45分。

为了方便,设下面所用的x数组均满足第一个条件。

\(F(x)=\Pi_{i=1}^{2m}x_i\),如果\(F(x)<n^m\),令\(x'=(\frac{n}{x_1},\frac{n}{x_2},...,\frac{n}{x_{2m}})\),可以发现所有的x'都满足\(F(x')>n^m\),且数量等于全部的\(F(x)>n^m\)的数量。由于x'与x是一一对应的,所以\(F(x)<n^m\)\(F(x)>n^m\)的方案数一样多。

设A表示\(F(x)<n^m\)的方案数,B表示\(F(x)=n^m\)的方案数,C表示\(F(x)>n^m\)的方案数。显然A+B+C的值为\(\sigma(n)^{2m}\)。而我们又已经证明了A=C,所以只要求出B,就可以求出A:
\[ A =\frac{A+B+C+B}{2}\\ =\frac{\sigma(n)^{2m}+B}{2} \]
那么B怎么求呢?
\(n={p_1}^{c_1}\cdot{p_2}^{c_2}\cdots{p_k}^{c_k}\),其中p都为质数,且两两不同。
\(f_{i,j}\)表示到了n的某个质因数,到了\(x_i\),已经选了j个\(p\)的方案数,这个很容易求。
最后把所有的 f 乘起来就是B了。

T3

赛时不懂样例,问DL们,不答,曰:“告诉你样例怎么求不就是白给了40分了吗?”。计算半日,无解,遂弃疗。

\(f_i\)表示从i的子节点走到i的期望步数,\(g_i\)表示从i的父节点走到i的期望步数。
那么状态转移方程显然(其中\(deg_i\)表示i的度,\(fa_i\)表示i的父节点):
\(f_u=\frac{1}{deg_u}+\Sigma_{x\in child_u}\frac{f_x+1+f_u}{deg_u}\)
\(g_u=\frac{1}{deg_{fa_u}}+\frac{1+g_u+g_{fa_u}}{deg_{fa_u}}+\Sigma_{x\in child_{fa_u}\bigwedge x\neq u}\frac{1+g_u+fx}{deg_{fa_u}}\)
移项后,转移方程中的\(f_u,g_u\)就可以被消去了:
\[ \begin{aligned} f_u & =\frac{1}{deg_u}+\sum_{x\in child_u}\frac{f_x+1+f_u}{deg_u}\\ f_u & =\frac{1}{deg_u}+\frac{fu(deg_u-1)}{deg_u}+\sum_{x\in child_u}\frac{f_x+1}{deg_u}\\ f_u-\frac{fu(deg_u-1)}{deg_u} & =\frac{1}{deg_u}+\sum_{x\in child_u}\frac{f_x+1}{deg_u}\\ \frac{deg_uf_u-fu(deg_u-1)}{deg_u} & =\frac{1}{deg_u}+\sum_{x\in child_u}\frac{f_x+1}{deg_u}\\ (deg_u-deg_u+1)f_u & =1+deg_u-1+\sum_{x\in child_u}f_x\\ f_u & =deg_u+\Sigma_{x\in child_u}f_x \end{aligned} \]
\[ \begin{aligned} g_u & =\frac{1}{deg_{fa_u}}+\frac{1+g_u+g_{fa_u}}{deg_{fa_u}}+\sum_{x\in child_{fa_u}\bigwedge x\neq u}\frac{1+g_u+f_x}{deg_{fa_u}}\\ g_u & =\frac{2}{deg_{fa_u}}+\frac{g_u(deg_{fa_u}-1)}{deg_{fa_u}}+\frac{g_{fa_u}}{deg_{fa_u}}+\sum_{x\in child_{fa_u}\bigwedge x\neq u}\frac{1+f_x}{deg_{fa_u}}\\ \frac{(deg_{fa_u}-1)g_u}{deg_{fa_u}} & = \frac{2+g_{fa_u}+deg_{fa_u}-2}{deg_{fa_u}}+\sum_{x\in child_{fa_u}\bigwedge x\neq u}\frac{f_x}{deg_{fa_u}}\\ g_u & = deg_{fa_u}+g_{fa_u}+\sum_{x\in child_{fa_u}\bigwedge x\neq u}f_x \end{aligned} \]
求结果时计算u到lca的所有f的和与v到lca的所有的g的和即可。


标程

T1

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100010
struct add
{
    int start,end,times;
}a[N],ans[N];
bool cmp(const add x,const add y)
{
    if(x.start!=y.start) return x.start<y.start;
    return x.end>y.end;
}
int main()
{
    freopen("range.in","r",stdin);
    freopen("range.out","w",stdout);
    int n,i,j,now,minus,last=0,times=0,l=0,lenth=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&now);
        if(now>last)
        {
            times+=now-last;
            a[++l]=(add){i,0,now-last};
        }
        else if(now<last)
        {
            minus=last-now;
            while(a[l].times<minus)
            {
                ans[++lenth]=(add){a[l].start,i-1,a[l].times};
                minus-=a[l].times,l--;
            }
            ans[++lenth]=(add){a[l].start,i-1,minus};
            a[l].times-=minus;
        }
        last=now;
    }
    while(l>0)
    {
        ans[++lenth]=(add){a[l].start,n,a[l].times};
        l--;
    }
    printf("%d\n",times);
    for(i=1;i<=lenth;i++)
        for(j=1;j<=ans[i].times;j++)
            printf("%d %d\n",ans[i].start,ans[i].end);
    return 0;
}

T2

#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define mod 998244353
ll f[250][3500],n,m,p,c,tt,sum=1;
inline ll min(ll x,ll y){return x<y?x:y;}
inline void dp()
{
    memset(f,0,sizeof(f));
    ll i,j,k;
    f[0][0]=1;
    for(i=1;i<=tt;i++)
        for(j=0;j<=c*m;j++)
            for(k=min(j,c);k>-1;k--)
                f[i][j]=(f[i][j]+f[i-1][j-k])%mod;
}
inline ll power(ll x,ll y)
{
    ll s=1;
    while(y)
    {
        if(y&1) s=s*x%mod;
        x=x*x%mod,y>>=1;
    }
    return s;
}
int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    ll i,j,k,b=1,inv;
    scanf("%lld%lld",&n,&m);
    tt=m<<1;
    for(i=2;i<=sqrt(n);i++)
        if(n%i==0)
        {
            p=i,c=0;
            while(n%i==0) n/=i,c++;
            sum=sum*(c+1)%mod,dp();
            b=b*f[tt][c*m]%mod;
        }
    if(n>1)
    {
        p=n,c=1;
        sum=sum*(c+1)%mod,dp();
        b=b*f[tt][c*m]%mod;
    }
    sum=power(sum,tt);
    inv=power(2,mod-2);
    printf("%lld\n",(sum+b)%mod*inv%mod);
    return 0;
}

T3

#include<cstdio>
#include<queue>
using namespace std;
#define ll long long
#define mod 1000000007ll
#define N 100010
struct edge
{
    ll end,next;
}a[N<<1];
struct node
{
    ll deep,id;
}now;
priority_queue<node>data;
bool close[N];
ll first[N],deg[N],deep[N],f[N],g[N],fa[N][20],n,m,s;
bool operator <(const node x,const node y){return x.deep<y.deep;}
inline void inc(ll x,ll y)
{
    deg[x]++,deg[y]++;
    a[++s]=(edge){y,first[x]};
    first[x]=s;
    a[++s]=(edge){x,first[y]};
    first[y]=s;
}
void makeTree(ll k,ll from)
{
    fa[k][0]=from;
    deep[k]=deep[from]+1;
    for(ll i=first[k];i;i=a[i].next)
        if(a[i].end!=from)
            makeTree(a[i].end,k);
}
void countf()
{
    ll i;
    while(!data.empty())
    {
        now=data.top();
        data.pop();
        (f[fa[now.id][0]]+=f[now.id])%=mod;
        if(!close[fa[now.id][0]])
        {
            close[fa[now.id][0]]=1;
            data.push((node){now.deep-1,fa[now.id][0]});
        }
    }
}
void countg(ll k)
{
    ll i,sum=0;
    for(i=first[k];i;i=a[i].next)
        if(a[i].end!=fa[k][0])
            sum+=f[a[i].end];
    for(i=first[k];i;i=a[i].next) if(a[i].end!=fa[k][0])
    {
        g[a[i].end]=deg[k]+g[k]+sum-f[a[i].end];
        countg(a[i].end);
    }
}
inline ll lca(ll u,ll v)
{
    ll i,ans=0;
    if(deep[u]<deep[v]) swap(u,v);
    for(i=17;i>=0;i--)
        if(deep[fa[u][i]]>=deep[v])
            u=fa[u][i];
    if(u==v) return u;
    for(i=17;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
void dfs(int k)
{
    f[k]=(f[k]+f[fa[k][0]])%mod,g[k]=(g[k]+g[fa[k][0]])%mod;
    for(int i=first[k];i;i=a[i].next)
        if(a[i].end!=fa[k][0])
            dfs(a[i].end);
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    ll i,j,u,v,ans;
    scanf("%lld%lld",&n,&m);
    for(i=1;i<n;i++)
    {
        scanf("%lld%lld",&u,&v);
        inc(u,v);
    }
    deep[0]=0;
    makeTree(1,0);
    for(i=2;i<=n;i++)
    {
        f[i]=deg[i];
        if(deg[i]==1)
            close[i]=1,data.push((node){deep[i],i});
    }
    close[0]=close[1]=1;
    countf();countg(1);
    dfs(1);
    for(j=1;j<18;j++)
        for(i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    while(m--)
    {
        scanf("%lld%lld",&u,&v);
        i=lca(u,v);
        ans=f[u]+mod-f[i]+g[v]+mod-g[i];
        if(ans) printf("%lld\n",ans%mod);
        else puts("0");
    }
    return 0;
}

转载于:https://www.cnblogs.com/huangzihaoal/p/11160987.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值