2019-7-27 考试总结

A. 随 (rand)

这个题考场上连$n^2dp$都没想出来,

忽略"原根"这个东西,没有它也能$AC$,

$n^2$的$dp$其实很好想的,用$dp[i][j]$表示在$i$时间$x$的值为$j$的概率,

那么这个$dp[i][j\times k\% p]+=dp[i-1][j]\times sum[k]\times inv[n] \% mod$,

然后就转移就可以了。

之后就到了倍增。。

直接预处理$g[i][j]$表示$2^i$时间的时候$x$的值为$j$的概率。

那么$g$很好转移,$g[i][j\times k\% p]+=g[i-1][j]\times g[i-1][k] \%mod$,

然后查询$f$就好了。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
#define mod 1000000007
#define Maxn 100050
#define Reg register
using namespace std;
long long n,m,p,inv,l,pow[1050],sum[1050];
long long g[150][1050],lss[5][1050];
long long mi(long long x,long long y,long long p)
{
    long long ans=1,base=x;
    while(y)
    {
        if(y&1) ans=(ans*base)%p;
        base=(base*base)%p;
        y>>=1;
    }
    return ans;
}
signed main()
{
//    freopen("text.in","r",stdin);
//    freopen("text2.out","w",stdout);
    scanf("%lld%lld%lld",&n,&m,&p);
    memset(g,0,sizeof(g));
    for(Reg int i=1,x;i<=n;++i)
    {
        scanf("%lld",&x); x%=p;
        ++sum[x];
    }
    inv=mi(n,mod-2,mod);
    pow[0]=1;
    for(Reg int i=1;i<=50;++i) pow[i]=pow[i-1]*2;
    //倍增预处理出2次的操作
    for(Reg int i=1;i<p;++i) g[0][i]=sum[i]*inv%mod;
    for(Reg int i=1;i<p;++i)
    {
        for(Reg int j=1;j<p;++j)
            g[1][i*j%p]=(g[1][i*j%p]+g[0][i]*sum[j]%mod*inv%mod)%mod;
    }
    //开始求g数组
    for(Reg int i=2;i<=50;++i)
    {
        for(Reg int j=1;j<p;++j)
        {
            for(Reg int k=1;k<p;++k)
            {
                if(j*k%p==0) continue;
                long long lll=(g[i-1][j]*g[i-1][k])%mod,o=j*k%p;
                g[i][o]=(g[i][o]%mod+lll)%mod;
            }
        }
    }
    //求f[m][]
    for(Reg int i=1;i<p;++i)
        lss[0][i]=(lss[0][i]+sum[i]*inv%mod)%mod;
    --m;
    long long k=0,sup=0;
    for(Reg int i=50;i>=0;--i)
    {
        if(pow[i]+k<=m)
        {
            k+=pow[i]; ++sup;
            int cur=sup&1,pre=(sup+1)&1;
            memset(lss[cur],0,sizeof(lss[cur]));
            for(Reg int j=1;j<p;++j)
                for(Reg int k=1;k<p;++k)
                    lss[cur][j*k%p]=(lss[cur][j*k%p]%mod+(lss[pre][j]*g[i][k])%mod)%mod;
        }
    }
    long long ans=0;
    for(Reg int i=1;i<p;++i)
        ans=(ans+lss[sup&1][i]*i%mod)%mod;
    printf("%lld",ans);
    return 0;
}
View Code

 

 

 

B. 单(single)

完全想不到

如果$opt=0$,那么进行$2$遍$dfs$,

第一遍求出根节点$1$的$b$值,第二遍由$1$节点递推到其他节点。

对于节点$i$,$b[i]=\sum a[j]\times dis[j][i]$,

那么对于$i$的儿子节点$x$,$x$的儿子节点到$x$的距离肯定比到$i$的距离小$1$,

然后其他节点差不多。

最后时间复杂度$O(n)$。

如果$opt=1$,

首先$b[i]-b[fat(i)]=SUM-2\times sum(i)$-----(*),

其中:

$SUM=\sum \limits_{i=1}^{n} a[i]$,

$sum(i)=\sum a[j]$,其中$j$是$i$的儿子节点。

这个可以感性的理解一下,

对于$i$节点的儿子节点(包括$i$节点),它到$i$节点的距离要比对$fat(i)$的距离少$1$,

所以它对$i$节点的贡献要少$a[j]$,所以要减去$sum(i)$。

而对于不是$i$节点的儿子节点的节点,它肯定相反,贡献要多$a[j]$,所以要加上$SUM-sum(i)$,

最后就是$SUM-2\times sum(i)$了。

然后列出了$n-1$个式子,就是那个(*)式子。

还要列最后一个式子$b[1]=\sum \limits_{i=2}^{n} sum(i)$,这个就是所有节点对根节点的贡献。

好了,列完式子了。。。

然后先把那$n-1$个式子相加:$\sum \limits_{i=2}^{n} b[i]-b[fat(i)]=(n-1)SUM-2\times \sum \limits_{i=2}^{n} sum(i)$,

然后就惊喜地发现这个右边的那一堆$\sum$就是$2\times b[1]$,

然后两边都加上$2\times b[1]$,

最后就是$2\times b[i]+\sum \limits_{i=2}^{n} b[i]-b[fat(i)]=SUM$,

然后就把$SUM$求出来了。

带入那$n-1$个式子可以得到$sum(i)$,不包括根节点$1$,

然后就树上差分了。

最后$a[1]$可以用总的$SUM$直接减去其他的。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define fabs(x) ((x)<0?(-1*(x)):(x))
#define Maxn 100050
#define INF 0x7ffffff
#define int long long
#define mod 1000000007
#define Reg register
using namespace std;
int A[Maxn],B[Maxn];
int t,n,m,opt,tot,fir[Maxn],vis[Maxn],size[Maxn],sigA[Maxn];
int sum[Maxn],SUM,S,lss;
struct Tu {int st,ed,next;} lian[Maxn*2];
vector<vector<int> >son(Maxn);
void add(int x,int y)
{
    lian[++tot].st=x;
    lian[tot].ed=y;
    lian[tot].next=fir[x];
    fir[x]=tot;
    return;
}
void dfs(int x,int deep) //先1遍dfs求所有点对根节点1的贡献
{
    size[x]=1;
    B[1]+=A[x]*deep;
    sigA[x]=A[x];
    vis[x]=1;
    for(Reg int j=fir[x];j;j=lian[j].next)
    {
        if(vis[lian[j].ed]) continue;
        son[x].push_back(lian[j].ed);
        dfs(lian[j].ed,deep+1);
        size[x]+=size[lian[j].ed];
        sigA[x]+=sigA[lian[j].ed];
    }
    return;
}
void dfs_re(int x) //第2遍dfs求所有点的A值
{
    for(Reg int i=0,to;i<son[x].size();++i)
    {
        to=son[x][i];
        B[to]=B[x]-sigA[to]+sigA[1]-sigA[to];
        dfs_re(son[x][i]);
    }
    return;
}
//每一个节点 b[i]-b[fat(i)]=SUM-2sum(i)
//b[1]=sigma(sum(2)~sum(n))
//n-1个式子相加:
//(n-1)SUM-2sigma(sum(2)~sum(n))=sigma(b[2]-b[fat(2)]~b[n]-b[fat(n)])
//                              =S
//加上2b[1]+S=(n-1)SUM
//得到(n-1)SUM,得到SUM=(2b[1]+S)/(n-1)
//带入原n-1个式子
//得到2sum(i),得到sum(i)
void dfs2(int x)
{
    vis[x]=1;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(vis[lian[i].ed]) continue;
        son[x].push_back(lian[i].ed);
        dfs2(lian[i].ed);
        S+=B[lian[i].ed]-B[x];
    }
    return;
}
void dfs2_re(int x)
{
    int sup=0;
    for(Reg int i=0;i<son[x].size();++i)
    {
        int to=son[x][i];
        sum[to]=(SUM-(B[to]-B[x]))/2;
        dfs2_re(to);
        sup+=sum[to];
    }
    A[x]=sum[x]-sup;
    if(x!=1) lss+=A[x];
    return;
}
signed main()
{
//    freopen("text.in","r",stdin);
    scanf("%lld",&t);
    while(t--)
    {
        lss=tot=S=SUM=0;
        scanf("%lld",&n);
        for(Reg int i=1;i<=n;++i)
        {
            fir[i]=sum[i]=A[i]=vis[i]=B[i]=size[i]=sigA[i]=0;
            son[i].clear();
        }
        for(Reg int i=1,x,y;i<=n-1;++i)
        {
            scanf("%lld%lld",&x,&y);
            add(x,y); add(y,x);
        }
        scanf("%lld",&opt);
        if(opt==0)
        {
            for(Reg int i=1;i<=n;++i) scanf("%lld",&A[i]);
            dfs(1,0); dfs_re(1);
            for(Reg int i=1;i<=n;++i) printf("%lld ",B[i]);
            printf("\n");
        }
        else
        {
            for(Reg int i=1;i<=n;++i)
            {
                scanf("%lld",&B[i]);
                SUM+=B[i];
            }
            dfs2(1); SUM=(S+2*B[1])/(n-1); dfs2_re(1);
            A[1]=SUM-lss;
            for(Reg int i=1;i<=n;++i) printf("%lld ",A[i]);
            printf("\n");
        }
    }
    return 0;
}
View Code

 

 

C. 题(problem)

这个题之前做过第一种情况,所以拿分比较容易,

第一种情况$opt=0$,没有限制,

直接枚举向上走的步数,然后其他三种情况可以表示出来,

这样最后就是$\sum \limits_{a,b,c,d}^{a,b,c,d>=0} C_{n}^{a}C_{n-a}^{b}C_{n-a-b}^{c}$,

直接求就可以了。

第二种情况$opt=1$,只能走$x$轴非负半轴,显然是个卡特兰数。(然而考试时想了半天)

直接卡特兰数求解就可以了,

最后就是$C_n^{n/2}-C_n^{n/2-1}$,

第三种情况$opt=3$,限制只能走坐标轴。

这个比较麻烦,然后考试时输出样例水到$5$分。

正解是$dp$,$F[i]=\Pi F[i-j]Cat(j/2-1)$,(然而蒟蒻并不知道为啥$Cat$要减一)

经过$scp-007$大神的指导,本蒟蒻终于理(yi)解(yin)出这个$dp$式子。

因为卡特兰数的这个栈是可以弹空的。。所以要先移动到$1$,然后再把$1$当作坐标轴继续移动。

对,就这么简单。

第四种情况也很简单,只能走第一象限和坐标轴非负半轴,

这个我考试的时候想到的是一个小容斥,具体看代码吧。

ans=(ans+C(n,a)*C(n-a,b)%mod*C(n-a-b,c)%mod)%mod;
ans=(ans-C(n,a)*C(n-a,b)%mod*C(n-a-b,(n-a-b)/2-1)%mod+mod)%mod;
ans=(ans-C(n,a+1)*C(n-a-1,b-1)%mod*C(n-a-b,c)%mod+mod)%mod;
ans=(ans+C(n,a+1)*C(n-a-1,b-1)%mod*C(n-a-b,(n-a-b)/2-1)%mod)%mod;

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#define abs(x) ((x)<0?(-1*(x)):(x))
#define C(x,y) (jc[x]*ny[y]%mod*ny[(x)-(y)]%mod)
#define Cat(x) ((C(2*(x),(x))-C(2*(x),(x)-1)+mod)%mod)
#define Maxn 200050
#define INF 0x7ffffff
#define mod 1000000007
#define Reg register
using namespace std;
int n,typ,st;
long long jc[Maxn],ny[Maxn],ans,dp[Maxn];
long long mi(long long x,long long y,long long p)
{
    long long ans=1,base=x;
    while(y)
    {
        if(y&1) ans=(ans*base)%p;
        base=(base*base)%p;
        y>>=1;
    }
    return ans;
}
void dfs(int x,int y,int tim)
{
    if(tim==n)
    {
        if(x==0&&y==0) ++ans;
        return;
    }
    if(x-1==0||y==0) dfs(x-1,y,tim+1);
    if(x==0||y-1==0) dfs(x,y-1,tim+1);
    if(x+1==0||y==0) dfs(x+1,y,tim+1);
    if(x==0||y+1==0) dfs(x-1,y,tim+1);
}
int main()
{
    scanf("%d%d",&n,&typ);
    ans=0;
    jc[0]=ny[0]=jc[1]=ny[1]=1;
    for(Reg int i=2;i<=n*2;++i)
    {
        jc[i]=jc[i-1]*i%mod;
        ny[i]=mi(jc[i],mod-2,mod);
    }
    if(typ==0)
    {
        for(Reg int i=0,a,b,c,d;i<=n;++i)
        {
            a=i,b=a,c=(n-2*a)/2,d=c;
            if(a<0||b<0||c<0||c<0) break;
            ans=(ans+C(n,a)*C(n-a,b)%mod*C(n-a-b,c)%mod)%mod;
        }
        printf("%lld",ans);
    }
    else if(typ==1)
    {
        ans=(C(n,n/2)-C(n,n/2-1)+mod)%mod;
        printf("%lld",ans);
    }
    else if(typ==2)
    {
        dp[0]=1,dp[2]=4;
        for(Reg int i=4;i<=n;i+=2)
        {
            for(Reg int j=2;j<=i;j+=2)
                dp[i]=(dp[i]+dp[i-j]*Cat(j/2-1)%mod)%mod;
            dp[i]=dp[i]*4%mod;
        }
        printf("%lld",dp[n]);
    }
    else if(typ==3)
    {
        for(Reg int i=0,a,b,c,d;i<=n;++i)
        {
            a=i,b=a,c=(n-2*a)/2,d=c;
            if(a<0||b<0||c<0||c<0) break;
            ans=(ans+C(n,a)*C(n-a,b)%mod*C(n-a-b,c)%mod)%mod;
            ans=(ans-C(n,a)*C(n-a,b)%mod*C(n-a-b,(n-a-b)/2-1)%mod+mod)%mod;
            ans=(ans-C(n,a+1)*C(n-a-1,b-1)%mod*C(n-a-b,c)%mod+mod)%mod;
            ans=(ans+C(n,a+1)*C(n-a-1,b-1)%mod*C(n-a-b,(n-a-b)/2-1)%mod)%mod;
        }
        printf("%lld",ans);
    }
    return 0;
}
View Code

 

 

总结:

考试一开始先看的$T1$,这。。样例是什么鬼

还有下边的“孙金宁教你学数学”,然后就感觉有点炸

赶紧跳到$T2$,首先第一眼,高斯消元,然后就打了一个$O(n^3)$的暴力,水到$30$分,

之后没多想去看$T3$,看到原题很开心。。$0,1,3$都非常简单,然后是$2$的那个点,

其实数据范围已经给了一些提示。。

然而我一直在想用组合数求解,最后依然没想出来,打了一个$dfs$,输出样例,水到$80$分。

考试还剩$40$多分钟,然后就回头看$T1$,样例模不对,然后一直在想这个样例怎么模,顺便打了一个$n^2$暴力,

直到最后,$wd$大神才公布这个样例不对。。

当时不到$10:20$,最后把那个$n^2$暴力交了上去,水到$10$分。

所以最后$10+30+80=120$。

没什么水平。。

转载于:https://www.cnblogs.com/Milk-Feng/p/11254516.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值