HZOI0727爆零赛

写在前面:

要吃早饭,不然头晕手抖不想写暴力分


考试概况:

T1 : 数学原根优化矩阵BOOST期望DP  pts:0

T2 : 树形DP,推式子大题  pts:0

T3 : 简单组数,DP  pts:80

sum:80  rank:26

其实T1能打10分,T2能打30分,但是头疼+牙周炎+手抖,打完T3就没心情了


考试流程:

看T1,原根?期望?走人

看T2,树形DP?没学过没写过,还要写DFS?我好懒啊放过我吧

看T3,组数?我好像还记得点,打吧,打点别爆零就行

打了2.5h发现自己好像能吃好多分.....打到typ=3不会,dp走人

然后回去翻了翻前面的题,一个都不想写,头疼睡了

考完试之后听HZ大佬吴迪说自己最后30min快速RUSHT1拿了50pts,瞬间感觉心态挺重要

但是什么都不会心态又有什么用呢


T1:随(rand)

期望绝赞不会中

以前打的期望题没一个能套上去的,我自闭了

下来题解TM看不懂一个字,都是啥啊

考场上看到有个n=1开开心心打个快速幂,但是好像理解错题意连10pts都没有

绝赞极度难过中


T2:单(single)

在套路上玩出新意的好题

有两种情况,第二种相对恶心,先来第一种

首先pick节点1来作为根,然后求出对于每个节点它的子树的点的权值和,

暴力求出根的答案,然后O(n)遍历树上所有点并换根DP,可以推一个简单式子来得到每个点的答案

对于第二种,通过儿子减父亲,可以得到 子树中的点权和 和 树上所有其他点权和 的 差,然后高斯消元

但是蒟蒻刚才去看了隔壁大佬的博客发现好像不用高斯消元,只要大力推式子即可,

下面部分转载自模拟赛9 SDFZ的RANK1 队爷tkj

我们取1号节点为根。
sum表示所有节点a值之和,size表示子树a值之和。
我们考虑从a求b的过程,因为我们原来是换根求的b数组,所以相邻两个节点的b值相减后为sum-2sizey
如果我们能知道sum的话所有值就能求出来了,但是要怎么求sum呢,考场上没想出来QAQ。
总是想着把它们加起来可以搞出sum,结果不行。
我们发现一号节点没有这个式子,所以我们把它的式子暴力写出来
b1=∑ni=1aidepi
我们惊奇的发现这个式子等于ni=2sizei
减一下就出来了。
考场上就差最后两行了,Orz

ni=1aidepi = ni=2sizei 我跪了,我没想到

看过别人博客之后才发觉自己的博客只有自己能看懂,但强人的博客别人也能看懂,Orz

考场上想到没学过,学过不会背板子,背了细节调不对,人真难啊

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 100010;
long long siz[MAXN],deep[MAXN],a[MAXN],b[MAXN],n,sum;
long long lef = 0;
struct Edge{
    int nxt,to;
}edge[MAXN<<1];
int head[MAXN],ectr;
void addedge(int from,int to){
    edge[++ectr].nxt =head[from];
    head[from] = ectr;
    edge[ectr].to = to;
}

void dfs1(int x,int fa){
    deep[x] = deep[fa] + 1;
    siz[x] = a[x];
    for(int i=head[x];i;i=edge[i].nxt){
        int to = edge[i].to;
        if(to == fa) continue;
        dfs1(to,x);
        siz[x] += siz[to];
    }
}

void dfs2(int x,int fa){
    b[x] = b[fa] - 2*siz[x] + siz[1];
    for(int i=head[x];i;i=edge[i].nxt){
        if(edge[i].to == fa) continue;
        dfs2(edge[i].to,x);
    }
}

void dfs3(int x,int fa){
    lef += b[x];lef -= b[fa];
    for(int i=head[x];i;i=edge[i].nxt){
        int to = edge[i].to;
        if(to == fa) continue;
        dfs3(to ,x);
    }
}

void dfs4(int x,int fa){
    siz[x] = (sum + b[fa] - b[x]) / 2;
    for(int i=head[x];i;i=edge[i].nxt){
        int to = edge[i].to;
        if(to == fa) continue;
        dfs4(to,x);
    }
}

void dfs5(int x,int fa){
    a[x] = siz[x];
    for(int i=head[x];i;i=edge[i].nxt){
        int to = edge[i].to;
        if(to == fa) continue;
        a[x] -= siz[to];
        dfs5(to,x);
    }
}

void solve1(){
    siz[1] = a[1];
    for(int i=head[1];i;i=edge[i].nxt){
        dfs1(edge[i].to,1);
        siz[1] += siz[edge[i].to];
    }
    for(int i=1;i<=n;i++){
        b[1] += a[i] * deep[i];
    }
    for(int i=head[1];i;i=edge[i].nxt){
        dfs2(edge[i].to,1);
    }
    for(int i=1;i<=n;i++){
        cout<<b[i]<<" ";
    }
    cout<<endl;
    return;
}

void solve2(){
    for(int i=head[1];i;i=edge[i].nxt){
        int to = edge[i].to;
        dfs3(to ,1);
    }
    lef += 2*b[1];
    sum = lef / (n - 1);
    for(int i=head[1];i;i=edge[i].nxt){
        int to = edge[i].to;
        dfs4(to,1);
    }
    long long evil = 0;
    for(int i=head[1];i;i=edge[i].nxt){
        int to = edge[i].to;
        evil += siz[to];
    }
    a[1] = sum - evil;
    siz[1] = sum;
    for(int i=head[1];i;i=edge[i].nxt){
        dfs5(edge[i].to,1);
    }
    for(int i=1;i<=n;i++){
        cout<<a[i]<<" ";
    }cout<<endl;
    return;
}

void _sweep(){
    memset(head,0,sizeof head);ectr = 0;lef = 0;sum = 0;
    memset(edge,0,sizeof edge);memset(a,0,sizeof a);memset(b,0,sizeof b);
    n=0;memset(siz,0,sizeof siz);memset(deep,0,sizeof deep);
}

int main(){
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--){
        _sweep();
        cin>>n;
        for(int i=1;i<n;i++){
            int a,b;
            cin>>a>>b;
            addedge(a,b);
            addedge(b,a);
        }
        int typ;
        cin>>typ;
        if(typ == 0){
            for(int i=1;i<=n;i++){
                cin>>a[i];
            }
            solve1();
        }
        if(typ == 1){
            for(int i=1;i<=n;i++){
                cin>>b[i];
            }
            solve2();
        }

    }
    return 0;
} 
T2 AC代码

 


 T3:题(problem)

四种情况

第一种:简单组合问题,但莫名其妙不优化式子跑不满25pts,我只有20pts

第二种:一眼卡特兰数,背的exgcd逆元组合数直接往上砸,拿分走人

第三种:观察到数据范围不一样,DP之魂燃烧,写码农代码

第四种:考场上想出来25pts然后自己否了自己,写了个10pts码农DP,后来听说是卡特兰数相乘,悔恨泪水流下来,

wdnmd老子调了一晚上终于A了,要预处理inv和fac

#include<bits/stdc++.h>

using namespace std;

const int mod = 1000000007;
int n,typ;
long long invv[200010],fac[200010];
int dpx[2][2100],dpy[2][2100];

long long qp(long long a,long long x){
    long long ret = 1;
    while(x){
        if(x&1) ret = (ret * a) % mod;
        a = (a * a) % mod;
        x >>= 1;
    }
    return ret;
}

void set_up(long long n){
    fac[0] = 1;
    for(int i=1;i<=n;i++){
        fac[i] = fac[i-1] * i % mod;
    }
    invv[n]=qp(fac[n],mod-2);
    for(int i=n-1;i>=0;i--){
        invv[i] = invv[i+1] * (i+1) % mod;
    }
    return;
}

long long C(long long n,long long m){
       return fac[n] * invv[n-m] % mod * invv[m] % mod;
}

long long catelan (long long n){
    return (C(2*n,n) - C(2*n,n-1) + mod) % mod;
}

int main(){
    ios::sync_with_stdio(false);
    cin>>n>>typ;
    set_up(n);
    if(typ == 0) {
        long long ans = 0;
        for(int i=0;i<=n/2;i++) {
            ans = ans + ((C(n,2*i) * C(2*i,i)) % mod + mod) * C(n-(2*i), (n-2*i)/2) % mod;
            ans %= mod;
        }
        cout<<ans<<endl;
        return 0;
    }
    if(typ == 1) {
        cout<<(long long)((C(n,n/2) - C(n,(n/2)-1)) + mod) % mod<<endl;
        return 0;
    }
    if(typ == 2){
        int del = 1010;
        dpx[0][del] = 1;
        dpy[0][del] = 1;
        for(int x=1;x<=n;x++){
            for(int j=1;j<=x;j++){
                dpx[x&1][j+del] = dpx[(x-1)&1][j+del+1];
                dpx[x&1][j+del] += dpx[(x-1)&1][j+del-1];
                dpx[x&1][j+del] %= mod;
                dpy[x&1][j+del] = dpy[(x-1)&1][j+del+1];
                dpy[x&1][j+del] += dpy[(x-1)&1][j+del-1];
                dpy[x&1][j+del] %= mod;
                dpx[x&1][del-j] = dpx[(x-1)&1][del-j+1];
                dpx[x&1][del-j] += dpx[(x-1)&1][del-j-1];
                dpx[x&1][del-j] %= mod;
                dpy[x&1][del-j] = dpy[(x-1)&1][del-j+1];
                dpy[x&1][del-j] += dpy[(x-1)&1][del-j-1];
                dpy[x&1][del-j] %= mod;
            }
            dpx[x&1][del] = dpx[(x-1)&1][del-1] + dpx[(x-1)&1][del+1];
            dpx[x&1][del] %= mod;
            dpx[x&1][del] += dpy[(x-1)&1][del-1];
            dpx[x&1][del] %= mod;
            dpx[x&1][del] += dpy[(x-1)&1][del+1];
            dpx[x&1][del] %= mod;
            dpy[x&1][del] = dpx[x&1][del];
        }
        cout<<dpx[n&1][del]<<endl;
        return 0;
    }
    if(typ == 3){
        long long ans = 0;
        for(int i=0;i<=n;i+=2){
            ans = (ans + C(n,i)*catelan(i/2)%mod*catelan((n-i)/2)%mod)%mod;
        }
        cout<<ans<<endl;
        return 0;
    }
}
T3 AC代码

 

这次爆零获得的经验:

1.对于一些式子特别长的题,在打式子的时候最好调用函数,好调好写,还缩短代码量,美滋滋(参考了tkj码风)

2.耐心推一推,写一写,调一调,就能拿到分的题,还是要好好下功夫的(比如T2),下考了也要练一练

3.进行决策,合理放弃(如T1)。


写在最后:

我想DKYgg了

TAG : SIN_XIII


 

posted @ 2019-07-27 12:02  SIN_XIII  阅读(145)  评论(0编辑  收藏  举报