ZROI NOIP21 冲刺汇总

同步发布于hriver2.github.io

「启」

实际上之前是单独发的,但是因为每天一测,不一定能够补完当天的题目,所以就将所有的未补完的考试中已经补过的题放在这里。

四道题全部整完的会单独发出。

UPD 2021.11.8

「Day1」

讲课,略。

「Day2」

✅ ZR-NOIP21-20D-D2

已经整理完,博客为:总之就是 | ZROI NOIP21 冲刺 Day2

「Day3」

⬜ ZR-NOIP21-20D-D3

「启」

今天在🐏的强码力支持下没有爆零(

缺省源和「这一篇」一致;

「A」

一道搞一搞出奇迹的题。

「A」题目简述

给出一个 \(3 \times 3\) 的矩阵,里面填入了 \(1\)\(9\),求问最少进行几次操作可以使得这个矩阵满足:每行三个数之和为 \(15\),每列三个数之和为 \(15\),且每条对角线三个数之和也为 \(15.\)

操作即为交换相邻的两个数,题目为 \(T,(T \le 50)\) 组询问。

「A」思路简述

经过手玩/爆搜,发现如果我们把矩阵表述为一个长为 \(9\) 的序列,最终只有 \(8\) 种目标序列:

\[2 7 6 9 5 1 4 3 8 \\ 2 9 4 7 5 3 6 1 8 \\ 4 3 8 9 5 1 2 7 6 \\ 4 9 2 3 5 7 8 1 6 \\ 6 1 8 7 5 3 2 9 4 \\ 6 7 2 1 5 9 8 3 4 \\ 8 1 6 3 5 7 4 9 2 \\ 8 3 4 1 5 9 6 7 2 \\ \]

于是我们考虑去双向 BFS.

于是考虑一下这样做的复杂度:一共有 \(12\) 种操作,一个矩阵最多扩展出 \(9!\) 种状态,也就是说复杂度为 \(O(8 \times 9!T).\)

但是这样是过不去的,于是考虑把这个 \(8\) 的常数干掉,于是乆有了对于目标矩阵的离散化,所以最终的时间复杂度为 \(O(9!T).\)

在 BFS 的过程之中用到了 Hash 去判断重复状态(

「A」Code

template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}

CI MOD(1000003),TXX(400000);

int n,Tco[10],f[10][10],sum,Sco[9][10],co[10],a[10];

int r[13][2];

void Pre()
{
    for(int i(1);i<=9;++i) Tco[i]=i;

    do
    {
        if (Tco[1]+Tco[2]+Tco[3] != 15 or Tco[4]+Tco[5]+Tco[6] != 15 or 
        Tco[7]+Tco[8]+Tco[9] != 15 or Tco[1]+Tco[4]+Tco[7] != 15 or 
        Tco[2]+Tco[5]+Tco[8] != 15 or Tco[3]+Tco[6]+Tco[9] != 15 or
        Tco[1]+Tco[5]+Tco[9] != 15 or Tco[3]+Tco[5]+Tco[7] != 15)
            continue;
        
        ++sum;

        for (int i(1);i<=9;++i) f[sum][i]=Tco[i],Sco[sum][Tco[i]]=i;

        puts("");

    } 
    while(next_permutation(Tco+1,Tco+10));

    r[1][0]=1,r[1][1]=2;
    r[2][0]=2,r[2][1]=3;
    r[3][0]=1,r[3][1]=4;
    r[4][0]=2,r[4][1]=5;
    r[5][0]=3,r[5][1]=6;
    r[6][0]=4,r[6][1]=5;
    r[7][0]=5,r[7][1]=6;
    r[8][0]=4,r[8][1]=7;
    r[9][0]=5,r[9][1]=8;
    r[10][0]=6,r[10][1]=9;
    r[11][0]=7,r[11][1]=8;
    r[12][0]=8,r[12][1]=9;
}

struct Core
{
    int id,ans;
};

struct Node
{
    int nxt,key,cnt;
}

t[TXX];int head[MOD+10],tot;

I void Add(int x,int k) {t[++tot]=(Node){(int)head[x%MOD],x,k};head[x%MOD]=tot;}

I bool CheckBool(int x)
{
    for (int i(head[x%MOD]);i;i=t[i].nxt)
        if (t[i].key==x)
            Heriko Romanno;

    Heriko Deltana;
}

I int CheckInt(int x)
{
    for (int i(head[x%MOD]);i;i=t[i].nxt)
        if (t[i].key==x)
            Heriko t[i].cnt;

    Heriko Deltana;
}

int m[10];

I int Modify(int x,int id)
{
    int temp(0),now,pw(1);

    for(int i(9);i;--i) m[i]=(x%10),x/=10;

    for (int i(9);i;--i)
    {
        now=m[i];

        if (now==r[id][1]) now=r[id][0];
        else if (now==r[id][0]) now=r[id][1];

        temp+=pw*now;pw*=10;
    }

    Heriko temp;
}

I void BFS()
{
    queue<Core> q;
    q.push((Core){123456789,0});

    while (!q.empty())
    {
        int x=q.front().id;
        int k=q.front().ans;
        q.pop();

        if (CheckBool(x)) continue;

        Add(x,k);

        for (int i=1;i<=12;++i)
        {
            int Ner(Modify(x,i));   
            q.push((Core){Ner,k+1});
        }
    }
}

int main()
{
    Files();
    Pre();BFS();

    int T;fr(T);

    while (T--)
    {
        for(int i(1);i<=9;++i) fr(a[i]);

        int ans(100);

        for(int i(1);i<=8;++i)
        {
            for(int j(1);j<=9;++j) co[j]=Sco[i][a[j]];

            int temp(0),pw(1);

            for(int j(9);j;--j) temp+=pw*co[j],pw*=10;

            ans = min(CheckInt(temp),ans);
        }

        fw(ans,1);
    }

    Heriko Deltana;
}

「Day4」

✅ ZR-NOIP-21-20D-D4

已经整理完,博客为:总之就是 | ZROI NOIP21 冲刺 Day4

「Day5」

⬜ ZR-NOIP21-20D-D5

「启」

昨天上大分,今天挂大分,就当是一场梦,醒来还是很感动

缺省源使用「V5」.

「A」

这题我场上写假了两个之后,又交了个假的上去……

「A」题目简述

对于长度同为 \(n\) 的数 \(a\)\(b\),定义其距离为 \(d(a,b) = \sum_{i=1}^n(a_i-b_i)^2.\)

现在你可以任意交换 \(a\) 的两个元素,求使得距离最短的最少交换次数。

求距离,若 \(opt=1\),输出最少交换次数。

数据范围:\(n \le 300000.\)

「A」思路简述

对于最短距离,sort 一下即可。

对于交换次数,我们用冰茶姬维护一下,再比较即可。

「A」Code

CI MXX(3e5+1),MOD(998244353);

struct Node
{
    int val,id;

    I bool operator < (const Node &co) const {Heriko val<co.val;}
}

a[MXX],b[MXX];

int n,opt,fa[MXX];

LL ans;

LL Find(int x)
{
    if(fa[x]!=x) fa[x]=Find(fa[x]);

    Heriko fa[x];
}

S main()
{
    Files();
    fr(n),fr(opt);

    for(int i(1);i<=n;++i) fr(a[i].val),a[i].id=i;

    for(int i(1);i<=n;++i) fr(b[i].val),b[i].id=i;

    for(int i(1);i<=n;++i) fa[i]=i;

    sort(a+1,a+1+n);sort(b+1,b+1+n);

    for(int i(1);i<=n;++i) (ans+=1ll*(a[i].val-b[i].val)*(a[i].val-b[i].val)%MOD)%=MOD,fa[Find(a[i].id)]=Find(b[i].id);

    fw((ans+MOD)%MOD,0);

    if(opt)
    {
        ans=0;

        for(int i(1);i<=n;++i) ans+=(fa[i]!=i);

        fw(ans,1);
    }

    Heriko Deltana;
}

「B」

场上想的是正解,但是因为做 A 做的心态炸了所以场上就没写出来(

「B」题目简述

国际象棋中,主教这个棋子可以攻击所有与其在同一条斜线上的位置(自己所处位置也算在内),现在在 \(n \times n\) 的棋盘中给出 \(m\) 个主教,问棋盘中还有几个不会被攻击到的位置。

数据范围:\(1 \le n,m \le 10^6.\)

「B」思路简述

因为是 \(10^6\),即使是三秒时限,\(O(n^2)\) 也铁定过不去,所以考虑 \(O(n)\) 的做法。

考虑到一个棋子带来的影响只在两条直线上,所以我们考虑这两条直线的解析式:\(y_1=x+b_1,y_2=-x+b_2.\)

于是我们只需要记录 \(b_1\)\(b_2\) 来判断边出现过没有即可。

因为所有的 \(y_1\) 都是平行的,所以我们只需要考虑其和所有 \(y_2\) 的交点即可,而这个可以前缀和优化。

于是乆做到了复杂度 \(O(n).\)

「B」Code

template<typename J>
I J Habs(const J &x) {Heriko x<0?-x:x;}

CI MXX(2e6+1);

int n,m,sum[MXX];

LL ans;

bitset<MXX> co[2];

S main()
{
    Files();
    fr(n),fr(m);

    for(int i(1),x,y;i<=m;++i)
    {
        fr(x),fr(y);
        int b1(x+y-1),b2(y-x+n);

        co[0][b1]=co[1][b2]=1;
    }
    
    n<<=1;

    for(int i(1);i<n;++i)
        if(co[0][i])
        {
            int temp(Habs((n>>1)-i));
            --sum[temp+1],++sum[n-temp+1];
        }	
        
    for(int i(3);i<n;++i) sum[i]+=sum[i-2];
    
    for(int i(1);i<n;++i)
        if(i<=(n>>1)) sum[i]+=i;
        else sum[i]+=n-i;
    
    for(int i(1);i<n;++i)
        if(co[1][i])
            sum[i]=0;
            
    for(int i(1);i<n;++i) ans+=sum[i];
    
    fw(ans,1);

    Heriko Deltana;
}

「Day6」

⬜ 21-NOIP21-20D-D6

「启」

今天这题给我做离谱了……希望几天之后的 CSP 不要这样出题……

各种毒瘤题,还卡常(

缺省源使用「V5」

「A」

这题场上因为读错题耽误了一个半小时,结果最后写完暴力发现又读错了题……我真是服了(

「A」题目复述

因为读错 \(114514\) 遍题,所以就不简述了吧(

「A」思路简述

因为第一页写在了第 \(t\) 页,根据题意,那么接下来的几天的页数会一天比一天小,然后会出现一个 \(p_i < p_{i+1}\)。然后去掉写过的这几页,又会重复这样的情况。

对于 \(q\) 中每一段连续的 \(q_i>q_{i+1}\),相当于就是要在剩下的页数里,选出若干页给这一段连续的 \(q_i>q_{i+1}\) 使用(由于第 \(1\) 页一定会选,所以计算组合数时上下都要减 \(1\)),于是就在排列 \(q\) 中找到所有 \(q_i<q_{i+1}\)\(i\) 构成一个长度为 \(len\) 的数列 \(w\),那么答案为:

\[\prod\limits_{i=2}^{len} \binom{n-w_{i-1}-1}{w_i-w_{i-1}-1} = (n-1)!\prod\limits_{i=2}^{len}\dfrac{1}{n-a_i}\cdot\dfrac{1}{(a_i-a_{i-1}-1)} \]

set 维护 \(w\),每次修改去修改前驱或后继即可。

「A」Code

我最一开始 ans 开了两个,一个全局,一个在 main 里面,就因为这个调了两个小时。

CI MXX(3e5+1),MOD(1e9+7);

int n,m,a[MXX];

LL fac[MXX],inv[MXX],val[MXX],ans(1);

bitset<MXX> co;

set<LL> s;

I LL FstPow(LL x,LL y)
{
    LL res(1);

    while(y)
    {
        if(y&1) (res*=x)%=MOD;

        (x*=x)%=MOD,y>>=1;
    }

    Heriko res;
}

I void Pre()
{
    fac[0]=1;

    for(int i(1);i<=n;++i) fac[i]=fac[i-1]*i%MOD;
 
    inv[n]=FstPow(fac[n],MOD-2);

    for(int i(n-1);i>=0;--i) inv[i]=inv[i+1]*(i+1)%MOD;
}

I LL C(int x,int y) {Heriko ((fac[x]*inv[y]%MOD)*inv[x-y])%MOD;}

I void Add(int x)
{
    auto i(s.lower_bound(x));
    auto lst(i);

    if(i==s.begin())
    {
        (ans*=C(n-1,x-1))%=MOD;
        (ans*=FstPow(val[(*i)],MOD-2))%=MOD;
        (ans*=C(n-x-1,(*i)-x-1))%=MOD;
        val[x]=C(n-1,x-1);
        val[(*i)]=C(n-x-1,(*i)-x-1);
    }
    else if(i==s.end())
    {
        --i;
        (ans*=C(n-(*i)-1,x-(*i)-1))%=MOD;
        val[x]=C(n-(*i)-1,x-(*i)-1);
    }
    else
    {
        --lst;
        (ans*=C(n-(*lst)-1,x-(*lst)-1))%=MOD;
        val[x]=C(n-(*lst)-1,x-(*lst)-1);
        (ans*=FstPow(val[(*i)],MOD-2))%=MOD;
        (ans*=C(n-x-1,(*i)-x-1))%=MOD;
        val[(*i)]=C(n-x-1,(*i)-x-1);
    }
    
    s.insert(x);
}

I void Del(int x)
{
    auto i(s.lower_bound(x));
    auto lst(i);
    auto org(i);

    if(i==s.begin())
    {
        (ans*=FstPow(val[x],MOD-2))%=MOD;
        ++i;
        (ans*=FstPow(val[(*i)],MOD-2))%=MOD;
        val[(*i)]=C(n-1,(*i)-1);
        (ans*=val[(*i)])%=MOD;
        val[x]=0;
    }
    else
    {
        --lst,++i;
        (ans*=FstPow(val[x],MOD-2))%=MOD;

        if(i!=s.end())
        {
            (ans*=FstPow(val[(*i)],MOD-2))%=MOD;
            (ans*=C(n-(*lst)-1,(*i)-(*lst)-1))%=MOD;
            val[(*i)]=C(n-(*lst)-1,(*i)-(*lst)-1);
        }
        
        val[x]=0;
    }

    s.erase(org);
}

S main()
{
    Files();
    fr(n),fr(m);Pre();

    for(int i(1);i<=n;++i) fr(a[i]);

    int lsti(0);

    for(int i(1);i<=n;++i)
    {
        co[i]=(a[i]<a[i+1]);
        
        if(co[i])
        {
            (ans*=C(n-lsti-1,i-lsti-1))%=MOD;
            val[i]=C(n-lsti-1,i-lsti-1);
            s.insert(i);lsti=i;
        }
    }
    
    fw(ans,1);

    while(m--)
    {
        int x,y;fr(x),fr(y);
        swap(a[x],a[y]);

        int lstq;
        
        if(x!=1)
        {  
            lstq=co[x-1];
            co[x-1]=(a[x-1]<a[x]);
            
            if(lstq != co[x-1]) co[x-1]?Add(x-1):Del(x-1);
        }

        if(x!=n)
        {
            lstq=co[x];
            co[x]=(a[x]<a[x+1]);

            if(lstq != co[x]) co[x]?Add(x):Del(x);
        }

        if(y!=1)
        {
            lstq=co[y-1];
            co[y-1]=(a[y-1]<a[y]);
            
            if(lstq != co[y-1]) co[y-1]?Add(y-1):Del(y-1);
        }

        if(y!=n)
        {
            lstq=co[y];
            co[y]=(a[y]<a[y+1]);
            
            if(lstq != co[y]) co[y]?Add(y):Del(y);
        }

        fw(ans,1);
    }

    Heriko Deltana;
}

「Day7」

⬜ 21-NOIP21-20D-D7

「启」

4h 做 A,结果多测清空不规范,爆零两行泪。

可是就快要复赛了啊……还在爆零……

缺省源使用「V5」.

「A」

4h,两个假算法,多测清空不规范,TLE,爆零。

「A」题目简述

你参加了一个为期 \(D\) 天的夏令营,在此 \(1\)\(D\) 天内有 \(N\) 个活动,其中第 \(i\) 个活动对你的吸引力是 \(h_i\) ,且将从第 \(s_i\) 天持续到第 \(e_i\) 天(包括 \(s_i\)\(e_i\))。

每天你最多参加 kk 个活动,你想知道在此期间,哪一天的活动对你的吸引力总和最大。

数据范围:\(1 \le N,D \le 3\times 10^5.\)

「A」思路简述

枚举时间,将当前可选的活动加入时间序列,维护两个堆,一个小根堆表示现在选了哪些课程保持大小为 \(k\),另一个堆维护剩下的随时准备补位。

「A」Code

template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}

CI MXX(3e5+2);

int d,n,k,h[MXX];LL ans;

bitset<MXX> in,no;

vector< pair<int,int> > v[MXX];

priority_queue< pair<int,int> > hp,thp;

I void Clear()
{
    for(int i(1);i<=d+1;++i) v[i].clear();

    while(hp.size()) hp.pop();

    while(thp.size()) thp.pop();

    in=no=0;
}

I void Solve()
{
    int opt(0),x(0),sz(0),val(0),num(0);
    LL sum(0);ans=0;

    for(int i(1);i<=d;++i)
    {
        for(auto j:v[i])
        {
            opt=j.first,x=j.second;

            if(opt)
            {
                if(sz<k)
                {
                    sum+=h[x];++sz;in[x]=1;
                    hp.push(make_pair(-h[x],x));
                }
                else
                {
                    while(no[hp.top().second]) hp.pop();

                    val=-hp.top().first;num=hp.top().second;

                    if(h[x]>val)
                    {
                        in[x]=1;in[num]=0;
                        sum+=(h[x]-val);
                        hp.pop();
                        hp.push(make_pair(-h[x],x));
                        thp.push(make_pair(val,num));
                    }
                    else thp.push(make_pair(h[x],x));
                }
            }
            else
            {
                no[x]=1;

                if(in[x])
                {
                    sum-=h[x];--sz;
                    
                    while (thp.size() && no[thp.top().second]) thp.pop();

                    if(thp.size())
                    {

                        val=thp.top().first,num=thp.top().second;thp.pop();

                        if(!no[num])
                        {
                            sum+=val,in[num]=1,++sz;
                            hp.push(make_pair(-val,num));
                        }
                    }
                }
            }
        }

        ans=Hmax(ans,sum);
    }
}

S main()
{
    Files();
    int T;fr(T);

    for(int ct(1);ct<=T;++ct)
    {
        Clear();fr(d),fr(n),fr(k);

        for(int i(1),l,r;i<=n;++i) 
        {
            fr(h[i]),fr(l),fr(r);

            v[l].push_back(make_pair(1,i));
            v[r+1].push_back(make_pair(0,i));
        }

        Solve();printf("Case #%d: %lld\n",ct,ans);
    }

    Heriko Deltana;
}

「Day8」

⬜ 21-NOIP21-20D-D8

「Day9」

⬜ 21-NOIP21-20D-D9

「启」

拉了。

缺省源使用「V5」.

「A」

场上想了一堆拉掉的玩意。

「A」题目简述

给定整数 \(N\),称它的一个分解为一组正整数 \(x_1,x_2,...,x_k\),满足以下条件:

  • \(\sum_{i = 1}^k x_i = N;\)

  • 对于任意 \(1 \le i \le k\),都有 \(x_i=2^a3^b\),这里 \(a,b\) 均为非负整数;

  • 对于任意 \(1 \le i,j \le k,i≠j\)\(x_i\) 都不是 \(x_j\) 的因子。

现在要求输出一种 \(N(N \le 10^{100})\) 的分解。

「A」思路简述

先将 \(N\) 不断除掉 \(2\),然后再不断减去最大的 \(3\) 的幂,答案最后要乘上那些 \(2.\)

这个数据范围需要高精(

「A」Code

CI MXX(105);

struct Pic
{
    int num[MXX],sz;

    Pic() {mst(num,0);sz=1;}

    I void Clear() {mst(num,0);sz=1;}

    I Pic operator * (const int &co) const
    {
        Pic res;res.sz=sz;

        for(int i(1);i<=sz;++i) res.num[i]=num[i]*co;

        for(int i(1);i<=sz;++i) res.num[i+1]+=(res.num[i]/10),res.num[i]%=10;

        while(res.num[res.sz+1])
        {
            ++res.sz;
            res.num[res.sz+1]+=(res.num[res.sz]/10);
            res.num[res.sz]%=10;
        }

        Heriko res;
    }

    I Pic operator * (const Pic &co) const
    {
        Pic res;res.sz=co.sz+sz;

        for(int i(1);i<=sz;++i)
            for(int j(1);j<=co.sz;++j)
            {
                res.num[i+j-1]+=(num[i]*co.num[j]);
                res.num[i+j]+=(res.num[i+j-1]/10);res.num[i+j-1]%=10;
            }

        while(!res.num[res.sz] and res.sz>1) --res.sz;

        Heriko res;
    }

    I Pic operator - (const Pic &co) const
    {
        Pic res;res.sz=sz;

        for(int i(1);i<=sz;++i)
        {
            res.num[i]+=num[i]-co.num[i];

            if(res.num[i]<0) --res.num[i+1],res.num[i]+=10;
        }

        while(!res.num[res.sz] and res.sz>1) --res.sz;

        Heriko res;
    }

    I void RightShift()
    {
        for(int i(sz);i;--i)
        {
            if(num[i]&1) num[i-1]+=10;

            num[i]>>=1;
        }

        while(!num[sz] and sz>1) --sz;
    }

    I bool operator > (const Pic &co) const
    {
        if(sz!=co.sz) Heriko sz>co.sz;

        for(int i(sz);i;--i)
            if(num[i]!=co.num[i])
                Heriko num[i]>co.num[i];

        Heriko Deltana;
    }
    
    I bool operator <= (const Pic &co) const {Heriko !((*this)>co);}

    I void Into(char s[])
    {
        sz=strlen(s+1);

        for(int i(1);i<=sz;++i) num[i]=s[sz-i+1]-'0';
    }

    I bool Zero() {Heriko (sz==1)&(num[1]==0);}

    I void fw() {for(int i(sz);i;--i) putchar(num[i]+'0'); putchar(' ');}
}

n,ans[1005];

char s[105];

int anslen;

S main()
{
    Files();

    scanf("%s",s+1);n.Into(s);Pic pw;pw.num[1]=1;

    while(!n.Zero())
    {
        while(!(n.num[1]&1)) pw=pw*2,n.RightShift();

        Pic res,nex;res.num[1]=1;nex=res*3;

        while(nex<=n) res=nex,nex=res*3;

        n=n-res;res=res*pw;ans[++anslen]=res;
    }

    fw(anslen,1);

    for(int i(1);i<=anslen;++i) ans[i].fw();

    Heriko Deltana;
}

「Day10」

讲课,略。

「Day11」

⬜ 21-NOIP21-20D-D11

「启」

A 假了,B 场上以为假了但是其实没假,总结还是拉了。

缺省源使用「V5」.

「A」

Bad.

「A」题目简述

有一颗 \(n(n \le 10^6)\) 个节点的树,每个节点可能是红色或者黑色,每次操作可以选择一个节点,把这个节点所连的同色的结点反色,求把整棵树变成同一种颜色的最小操作次数。

「A」思路简述

因为相邻同色的结点可以看作一块,所有缩为一个点,然后对新树找直径,答案即为直径除 \(2.\)

「A」Code

template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}

template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}

CI MXX(1e6+1);

int n,f[MXX],ans;

struct Node
{
    int nex,to;
}

r[MXX<<1];int cnt,head[MXX];

I void Add(int x,int y)
{
    r[++cnt]=(Node){head[x],y};head[x]=cnt;
    r[++cnt]=(Node){head[y],x};head[y]=cnt;
}

bitset<MXX> co;

void DFS(int x,int fa)
{
    for(int i(head[x]);i;i=r[i].nex)
    {
        int y(r[i].to);

        if(y==fa) continue;

        DFS(y,x);

        if(co[y]!=co[x]) 
        {
            ans=Hmax(ans,f[x]+f[y]+1);
            f[x]=Hmax(f[x],f[y]+1);
        }
        else
        {
            ans=Hmax(ans,f[x]+f[y]);
            f[x]=Hmax(f[x],f[y]);
        }
    }
}

I int GetCo()
{
    char c(getchar());

    while(c!='R' and c!='B') c=getchar();

    Heriko c=='R'?1:0;
}

S main()
{
    Files();

    fr(n);

    for(int i(1);i<=n;++i) co[i]=GetCo();

    for(int i(1),x,y;i<n;++i) fr(x),fr(y),Add(x,y);

    DFS(1,0);fw((ans+1)>>1,1);

    Heriko Deltana;
}

「B」

场上最后的贪心策略是对的,但是当时以为假了。

「B」题目简述

给出一个长度为 \(k\) 的数列,然后给出 \(n\) 个操作,操作分为三种:

  • \(a_i=b\)
  • \(a_i=a_i+b\)
  • \(a_i=a_i \times b\)

其中 \(i,b\) 是给定的,每个操作只能用一次,最多使用 \(m\) 个操作,让整个数列的乘积最大。

「B」思路简述

考虑将 Cover 操作转为 Add 操作,然后贪心的选取贡献权值大的即可。

「B」Code

template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}

template<typename J>
I J Hmin(const J &x,const J &y) {Heriko x<y?x:y;}

CI MXX(1e5+1),MOD(1e9+7);

int n,k,m,cnt;

LL a[MXX],ans(1),cover[MXX];

struct CoFuc
{
    int opt,pos;LL val;long double dlt;
}

co[MXX],ro[MXX];

I bool CMP(const CoFuc &x,const CoFuc &y) {Heriko x.val>y.val;}

I bool RMP(const CoFuc &x,const CoFuc &y) {Heriko x.dlt>y.dlt;}

S main()
{
    Files();

    fr(k),fr(n),fr(m);

    for(int i(1);i<=k;++i) fr(a[i]);

    for(int i(1);i<=n;++i)
    {
        fr(co[i].opt),fr(co[i].pos),fr(co[i].val);

        if(co[i].opt==1) cover[co[i].pos]=Hmax(cover[co[i].pos],co[i].val);
    }

    for(int i(1);i<=k;++i)
        if(cover[i]>a[i])
            ro[++cnt]=(CoFuc){2,i,cover[i]-a[i],0.0};

    for(int i(1);i<=n;++i)
        if(co[i].opt!=1)
            ro[++cnt]=co[i];
    
    sort(ro+1,ro+1+cnt,CMP);

    for(int i(1);i<=k;++i) cover[i]=a[i];

    for(int i(1);i<=cnt;++i)
        if(ro[i].opt==2)
        {
            ro[i].dlt=1.0*(cover[ro[i].pos]+ro[i].val)/(1.0*cover[ro[i].pos]);
            cover[ro[i].pos]+=ro[i].val;
        }
        else if(ro[i].opt==3) ro[i].dlt=ro[i].val*1.0;

    sort(ro+1,ro+1+cnt,RMP);

    for(int i(1);i<=Hmin(m,cnt);++i)
        if(ro[i].opt==2) a[ro[i].pos]+=ro[i].val;
        else if(ro[i].opt==3) (ans*=ro[i].val)%=MOD;

    for(int i(1);i<=k;++i) (ans*=a[i])%=MOD;

    fw((ans+MOD)%MOD,1); 

    Heriko Deltana;
}

「C」

不是这怎么还出特判题呐?

「C」题目简述

小明有 \(1\)\(n\)\(n\) 包零食,同时他又有 \(1\)\(n\)\(n\) 个朋友。

昨天,小明的 \(n\) 个朋友都到他家来玩了。他的 \(n\) 个朋友瓜分了他的 \(n\) 包零食,每个人都恰好吃了一包零食,没有两个人吃了同一包零食。

小明发现,第 \(i\) 个朋友吃第 \(j\) 包零食能获得的愉悦值是 \(i\bmod j\)

今天,小明想回忆起每个朋友吃的是哪包零食,他想不起来了,但是他却记得了所有人的愉悦值之和 \(s\)。于是,小明找上了你,请你构造出一种可能的方案。

由于小明记忆力不好,他有可能记错了 \(s\),所以可能会存在无解的情况。

「C」思路简述

特判题(

  • \(s\) 的上界为 \(\dfrac{n \times (n+1)}{2}\),超过乆输出无解。

  • \(s=0,1,2,\dfrac{n \times (n+1)}{2}-1\) 的时候详见代码,\(n\le 3\) 时同理。

  • 剩余情况我们只需要构造出 \(2 \to n-1\) 的答案。

「C」Code

这里的 fw 函数不太一样,放一下。

template<typename J>
I void fw(bool g,J x,bool k)
{
    if(!g)
    {
        puts("SPFA is dead!");

        Heriko;
    }

    if(x<0) x=-x,putchar('-');

    static short stak[35];short top(0);

    do
    {
        stak[top++]=x%10;
        x/=10;
    }
    while(x);

    while(top) putchar(stak[--top]+'0');

    k?puts(""):putchar(' ');
}

CI MXX(1e6+1);

LL n,s,res,ans[MXX],flg;

bitset<MXX> co;

S main()
{
    Files();

    fr(n),fr(s);

    if(s>((n*(n-1))>>1)) Heriko fw(0,114514,1919810),Deltana;
    
    if(n==1)
    {
        if(!s) puts("1");
        else Heriko fw(0,114514,1919810),Deltana;

        Heriko Deltana;
    }
    else if(n==2)
    {
        if(!s) puts("1\n2");
        else if(s==1) puts("2\n1");
        else Heriko fw(0,114514,1919810),Deltana;

        Heriko Deltana;
    }
    else if(n==3)
    {
        if(!s) puts("1\n2\n3");
        else if(s==1) puts("2\n1\n3");
        else if(s==2) puts("3\n1\n2");
        else if(s==3) puts("1\n3\n2");
        else Heriko fw(0,114514,1919810),Deltana;

        Heriko Deltana;
    }
    
    if(!s)
    {
        for(int i(1);i<=n;++i) fw(1,i,1);

        Heriko Deltana;
    }
    else if(s==1)
    {
        puts("2\n1");

        for(int i(3);i<=n;++i) fw(1,i,1);

        Heriko Deltana;
    }
    else if(s==2)
    {
        puts("3\n1\n2");

        for(int i(4);i<=n;++i) fw(1,i,1);

        Heriko Deltana;
    }
    else if(s==((n*(n-1))>>1)-1)
    {
        if(n&1)
        {
            ans[1]=3,ans[2]=1,ans[n]=2;

            for(int i(3);i<n;++i) ans[i]=i+1;
        }
        else
        {
            ans[1]=1;ans[n]=2;

            for(int i(2);i<n;++i) ans[i]=i+1;
        }

        for(int i(1);i<=n;++i) fw(1,ans[i],1);

        Heriko Deltana;
    }

    ans[n]=1;--s;

    for(int i(n-1);i>=2;--i)
    {
        if(s>=i and s-i!=1) s-=i;
        else ans[i]=i,co[i]=1;
    }

    int nw(n);

    for(int i(n-1);i;--i)
        if(!ans[i]) 
        {
            ans[i]=nw,co[nw]=1;

            while(co[nw]) --nw;
        }

    for(int i(1);i<=n;++i) fw(1,ans[i],1);

    Heriko Deltana;
}

「Day12」

✅ 21-NOIP21-20D-D12

已经整理完,博客为:总之就是 | ZROI NOIP21 冲刺 Day12

「Day13」

⬜ 21-NOIP21-20D-D13

「启」

今天倒是没挂 A,但是大家都切 A,所以我挂 B 了/cy

缺省源使用「V5」.

「A」

切了好耶(

Time:1s Memory:512MiB.

「A」题目简述

要求找出满足下列条件最小的数:

  • 这是一个正整数;

  • 这个数至少有四个不同的因子;

  • 这个数的任意两个因子之间的差不小于输入的 \(n(n \le 10^5)\)

「A」思路简述

因为要求两个任意两个因子之间的差不小于输入的 \(n\),所以想到选取质数。

而题目要求至少要有四个不同的因子,而一个数最小的因子是 \(1\),最大的因子是自己,所以我们只需要找到大于 \(n+1\) 的第一个质数和大于等于第一个选出来的质数 \(+n\) 后的最小的质数即可。

用一个欧拉筛解决质数问题即可。

「A」Code

CI MXX(1e7+5);

LL prime[MXX],cnt;

bool nopr[MXX];

I void Es(LL x)
{
    nopr[1]=1;

    for(LL i(2);i<=x;++i)
    {
        if(!nopr[i]) prime[++cnt]=i;

        for(int j(1);j<=cnt and prime[j]*i<=x;++j)
        {
            nopr[i*prime[j]]=1;

            if(i%prime[j]==0) break;
        }
    }
}

LL n,T;

S main()
{
    Files();

    Es(MXX);

    fr(T);

    while(T--)
    {
        fr(n);LL fst(0),fstid(0),sed(0);

        for(int i(1);i<=cnt;++i)
            if(prime[i]>=n+1)
                {
                    fst=prime[i];
                    fstid=i;
                    
                    break;
                }

        for(int i(fstid);i<=cnt;++i)
            if(prime[i]>=fst+n)
            {
                sed=prime[i];

                break;
            }

        fw(sed*fst,1);
    }

    Heriko Deltana;
}

「B」

挂了好耶(

Time:1s Memory:512MiB.

「B」题目简述

给出一个有 \(n(n\le 1000)\) 个点的无向完全图,每次在图上去掉一个生成树中的所有边,问最多能进行多少次,并且输出每次要删掉哪些边。

「B」思路简述

首先能知道 \(n\) 个点的完全图的总边数为 \(\dfrac{n(n-1)}{2}\),而一个生成树的边数为 \(n-1\),所以能得出最多能进行 \(\left\lfloor\frac{n}{2}\right\rfloor\) 次删除操作。

那么下面考虑如何构造出每次删掉的生成树,场上是想的 DFS 删除,但是没判环于是挂掉了。

考完之后看题解发现可以反着来,考虑从 \(n-2\) 转移到 \(n\) 的时候,对于之前的每一个生成树 \(i\) 连上 \(2i-1\)\(n-1\)\(2i\)\(n\),然后如果是偶数就再建一个新的,连边同理。

「B」Code

int n,m;

S main()
{
    Files();

    int T;fr(T);

    for(int ct(1);ct<=T;++ct)
    {
        fr(n);m=(n>>1);
        printf("Case #%d: %d\n",ct,m);

        for(int i(1);i<=m;++i)
        {
            if(!(n&1)) fw((i+m)%n+1,0),fw(i+m,1);

            for(int j(1);j<=(n-1)>>1;++j)
            {
                fw((i-j+n)%n+1,0),fw((i+j-1)%n+1,1);
                fw((i+j-1)%n+1,0),fw((i-j+n-1)%n+1,1);
            }
        }
    }

    Heriko Deltana;
}

「C」

写不出 Check 好耶(

Time:3s Memory:512MiB.

「C」题目简述

现在要将一个 \(n(n\le10^5)\) 个结点的无向树分割成 \(k\) 个子树,使得这些子树中结点权值和最大的一颗最小。

「C」思路简述

这个题的答案求解……嗯是二分没错了,那么下面的问题就是如何去写 Check

然而场上没写出来 T_T

所以这个的 Check 怎么写呢(

考虑 DFS 去解决(

\(f(i)\) 表示当前结点 \(i\) 所在的联通块的点权和。对于每一个节点,将其所有孩子的 \(f\) 排序以后,从小到大依次往父亲里塞。

塞不下的,就只能切断了,也就是形成单独的联通块,塞进父亲里的,更新到父亲的 \(f\) 中,可以继续和上面的节点合并,然后就能求出最大值。

排序和统计用个 vector 即可。

「C」Code

template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}

CI MXX(1e5+1);

struct Node
{
    int nex,to;
}

r[MXX<<1];int cnt,head[MXX];

I void Add(int x,int y)
{
    r[++cnt]=(Node){head[x],y};head[x]=cnt;
    r[++cnt]=(Node){head[y],x};head[y]=cnt;
}

int n,k;

LL val[MXX],ans,f[MXX],sz[MXX];

vector<LL> sub[MXX];

void DFS(LL lmt,int x,int fa)
{
    f[x]=sz[x]=0;sub[x].clear();

    int res(0);sz[x]=val[x];

    for(int i(head[x]);i;i=r[i].nex)
    {
        int y(r[i].to);

        if(y==fa) continue;

        DFS(lmt,y,x);++res;sub[x].push_back(sz[y]);f[x]+=f[y];
    }

    sort(sub[x].begin(),sub[x].end());

    for(auto i:sub[x])
        if(sz[x]+i<=lmt) sz[x]+=i,--res;
        else break;

    f[x]+=res;
}

I bool Check(LL x)
{
    for(int i(1);i<=n;++i)
        if(x<val[i])
            Heriko Deltana;

    DFS(x,1,0);

    Heriko f[1]<k;
}

S main()
{
    Files();

    int T;fr(T);

    for(int ct(1);ct<=T;++ct)
    {
        fr(n),fr(k);cnt=0;mst(head,0);

        for(int i(1),x,y;i<n;++i) fr(x),fr(y),Add(x,y);

        LL sm(0),mx(0);

        for(int i(1);i<=n;++i) fr(val[i]),sm+=val[i],mx=Hmax(mx,val[i]);

        if(n==k)
        {
            printf("Case #%d: %lld\n",ct,mx);

            continue;
        }

        LL l(0),r(sm);

        while(l<=r)
        {
            LL mid((l+r)>>1);

            if(Check(mid)) r=mid-1,ans=mid;
            else l=mid+1;
        }

        printf("Case #%d: %lld\n",ct,ans);
    }

    Heriko Deltana;
}

「Day14」

⬜ 21-NOIP21-20D-D14

「启」

哈哈又啥都没有,还是使用惊艳骗分。

缺省源使用「V5」.

「A」

不知道该说啥。

Time:2s,Memory:512MiB.

「A」题目简述

求一颗 \(n(n \le 2000)\) 的树上有多少点集 \(S\) 内的任意两点的最短距离为 \(d.\)

「A」思路简述

先处理出深度,然后按照深度从浅到深跑 DFS,每次选取之前已经选取过的点,然后计算子集大小 \(O(1)\) 回答询问。

「A」Code

CI MXX(2001),MOD(1e9+7);

struct Node
{
    int nex,to;
}

r[MXX<<1];int cnt,head[MXX];

I void Add(int x,int y)
{
    r[++cnt]=(Node){head[x],y};head[x]=cnt;
    r[++cnt]=(Node){head[y],x};head[y]=cnt;
}

int n,dep[MXX],id[MXX];

LL pw2[MXX],tot[MXX],ans[MXX];

void DFS1(int x)
{
    for(int i(head[x]);i;i=r[i].nex)
        if(!dep[r[i].to])
            dep[r[i].to]=dep[x]+1,DFS1(r[i].to);
}

bitset<MXX> vis;

void DFS2(int x,int fa,int stp)
{
    if(vis[x]) ++tot[stp];

    for(int i(head[x]);i;i=r[i].nex)
        if(r[i].to!=fa)
            DFS2(r[i].to,x,stp+1);
}

S main()
{
    Files();

    fr(n);

    for(int i(1);i<=n;++i) id[i]=i;

    pw2[0]=1;

    for(int i(1);i<=n;++i) pw2[i]=(pw2[i-1]<<1)%MOD;

    for(int i(1),x,y;i<n;++i) fr(x),fr(y),Add(x,y);

    dep[1]=1;DFS1(1);sort(id+1,id+1+n,[](int x,int y){Heriko dep[x]<dep[y];});

    for(int i(1);i<=n;++i)
    {
        vis[id[i]]=1;int tmp(1);
        memset(tot,0,sizeof(LL)*(n+1));
        DFS2(id[i],0,0);

        for(int j(1);j<=n;++j)
        {
            (ans[j]+=(pw2[tot[j]]-1)*(pw2[tmp-1]))%=MOD;
            tmp+=tot[j];
        }
    }

    int T;fr(T);

    while(T--)
    {
        int x;fr(x);fw(ans[x],1);
    }

    Heriko Deltana;
}

「Day15」

⬜ 21-NOIP21-20D-D15

「启」

哈哈又挂了 A,还是只有垃圾 \(10\) 分暴力。

缺省源使用「V5.1」.

「A」

挂了。

Time:1s,Memory:1GiB.

「A」题目简述

\(n(n \le 50)\) 个数中选出一个非空子集,求满足异或和等于与和,每个数都满足 \(\le 2^13.\)

「A」思路简述

我们设 \(f(i,j,k)\) 表示选取前 \(i\) 个数,与和为 \(j\),异或和为 \(k\) 的方案数,那么显然答案为 \(\sum\limits_{i=1}^{2^{n}}f(i,j,k)\times[j=k].\)

因为直接开会炸空间,所以我们滚掉第一维,状态就变成了 \(f(0/1,j,k)\),然后我们用一个栈来优化一下复杂度,要不然还是过不去(

「A」Code

CI NXX(51),MXX(1<<13),SXX(1<<21);

bitset<MXX> co[MXX]; 

int n,m,a[NXX],top[2];

LL f[2][MXX][MXX];

pair< int,int > stk[2][SXX];

LL ans;

S main()
{
    Files();

    fr(n);

    for(int i(1);i<=n;++i) fr(a[i]),m|=a[i];

    int nw(0);f[nw][m][0]=1;stk[nw][++top[nw]]=mkp(m,0);

    for(int i(1),x,y;i<=n;++i)
    {
        nw^=1;int lst(nw^1);

        for(int j(1);j<=top[nw];++j) x=stk[nw][j].first,y=stk[nw][j].second,f[nw][x][y]=0;

        top[nw]=0;

        for(int j(0);j<=m;++j) co[j]=0;

        for(int j(1);j<=top[lst];++j)
        {
            x=stk[lst][j].first,y=stk[lst][j].second;
            f[nw][x&a[i]][y^a[i]]+=f[lst][x][y];

            if((x&a[i])==(y^a[i])) ans+=f[lst][x][y];

            f[nw][x][y]+=f[lst][x][y];

            if(!co[x][y]) co[x][y]=1,stk[nw][++top[nw]]=mkp(x,y);

            x&=a[i],y^=a[i];

            if(!co[x][y]) co[x][y]=1,stk[nw][++top[nw]]=mkp(x,y);
        }       
    }
    
    fw(ans,1);

    Heriko Deltana;
}

「Day16」

⬜ 21-NOIP21-20D-D16

因为某些原因未参加。

「SC1」

⬜ 21-NOIP21-20D-SC1

同上。

「SC2」

⬜ 21-NOIP21-20D-SC2

「启」

因为某些事情,前几场考试没参加,这一场听说是信心场还 unr,就打一打玩。

缺省源使用「V5.2」.

「A」

诈骗题,但是场上写的结论少了点东西所以只有 \(80.\)

「A」题目简述

一个长度为 \(N\) 的字符串,字典集大小为 \(M\),要求连续长度为 \(K\) 的子串都必须是回文串,求方案数。

「A」思路简述

看起来没啥东西所以就觉得很诈骗(

分为以下几种情况:

  • \(k>n\)\(k=1\) 时,答案为 \(m^n.\)

  • \(k=n\) 时,答案为 \(m^{\frac{n-1}{2}}.\)

  • 否则,当 \(k\)\(2\) 的倍数的时候,答案为 \(m^2\),若不是 \(2\) 的倍数,答案为 \(m.\)

「A」Code

CI MOD(1e9+7);

int n,m,k;

I int FstPow(int x,int y)
{
    int res(1);

    while(y)
    {
        if(y&1)
            (res*=1ll*x)%=MOD;
        
        (x*=1ll*x)%=MOD;
        y>>=1;
    }

    Heriko res;
}

S main()
{
    Files();

    fr(n),fr(m),fr(k);

    if(k>n or k==1)
        fw(FstPow(m,n)%MOD,1);
    else if(k==n)
        fw(FstPow(m,(n+1)>>1),1);
    else if(k&1)
        fw((m*m)%MOD,1);
    else if(!(k&1))
        fw((m+MOD)%MOD,1);
    
    Heriko Deltana;
}

「B」

考场写了个暴力,然后乆润了。

「B」题目简述

每次可以在矩阵中选择一行或一列加到答案中,然后把选择的这一列的每一项都减去 \(p.\)

「B」思路简述

考虑把行和列的操作拆开先预处理,然后最后合并答案的时候减去互相的影响。

「B」Code

template<typename J>
I J Hmax(const J &x,const J &y)
{
    Heriko x>y?x:y;
}

CI MXX(1001),KXX(1e5+1);
CL INF(1145141919810);

int n,m,k,p,a[MXX][MXX];

LL ans(-INF),f[KXX],coi[KXX],coj[KXX],g[KXX];

priority_queue<LL> q;

S main()
{
    Files();

    fr(n),fr(m),fr(k),fr(p);

    for(int i(1);i<=n;++i)
        for(int j(1);j<=m;++j)
            fr(a[i][j]);

    for(int i(1);i<=n;++i)
        for(int j(1);j<=m;++j)
            coi[i]+=a[i][j],coj[j]+=a[i][j];

    for(int i(1);i<=n;++i)
        q.push(coi[i]);

    for(int i(1);i<=k;++i)
    {
        LL x(q.top());
        q.pop();
        f[i]=f[i-1]+x,x-=p*m;
        q.push(x);
    }

    while(q.size())
        q.pop();

    for(int i(1);i<=m;++i)
        q.push(coj[i]);

    for(int i(1);i<=k;++i)
    {
        LL x(q.top());
        q.pop();
        g[i]=g[i-1]+x,x-=p*n;
        q.push(x);
    }

    for(int i(0);i<=k;++i)
        ans=Hmax(ans,f[i]+g[k-i]-(1ll*i*(k-i)*p));

    fw(ans,1);

    Heriko Deltana;
}

「C」

是个大模拟,先不补。

「Day17」

⬜ 21-NOIP21-20D-D17

缺省源使用「V5.2」.

「启」

没啥好说的。

「A」

场上做出来了。

题目

「A」思路简述

我们考虑对于每个点都用两个长度为 \(m\) 的二进制数来存储,分别记录第 \(i\) 位对应着第 \(i\) 次操作的时候的 \(X\)\(Y\) 归属状态,那么最终两个点 \(i,j\) 之间是否有连边就等同于 \(X_i \operatorname{and} Y_j\)\(1\) 的个数是否为奇数加上 \(X_j \operatorname{and} Y_i\)\(1\) 的个数再减去两者交集。

「A」Code

CI NXX(2e4+1),MXX(64);

bitset<MXX> X[NXX],Y[NXX];

int n,m;

LL ans;

char s[NXX];

S main()
{
    Files();

    fr(n),fr(m);

    for(int i(1);i<=m;++i)
    {
        scanf("%s",s+1);

        for(int j(1);j<=n;++j)
        {
            Y[j][i]=(s[j]^48)&1;
            X[j][i]=((s[j]^48)>>1)&1;
        }
    }

    for(int i(1);i<n;++i)
        for(int j(i+1);j<=n;++j)
            ans+=((X[i]&Y[j]).count()+((Y[i]&X[j])^((X[i]&Y[j])&(Y[i]&X[j]))).count())&1;

    fw(ans,1);
    
    Heriko Deltana;
}

「B」

题目

「B」思路简述

看到这个问题,就很自然地想到二分,于是设 \(b_i = a_i - x\),然后去二分这个 \(x.\)

然后把 \(b\) 的前缀和求出来,求长度为 \(k\) 的 LIS.

「B」Code

template<typename J>
I J Hmax(const J &x,const J &y)
{
    Heriko x>y?x:y;
}

CI MXX(2e5+1);

const double INF(1e9);

int n,k,a[MXX],mx;

double ans,b[MXX],f[MXX];

I bool Check(double x)
{
    for(int i(1);i<=n;++i)
        b[i]=b[i-1]+a[i]-x;

    int hd(0);
    f[hd]=INF;
    
    for(int i(1);i<=n;++i)
    {
        if(b[i]<0.0 or b[i]>b[n])
            continue;
        
        int pos(lower_bound(f,f+1+hd,b[i])-f);
        f[pos]=b[i];

        if(pos==hd)
            f[++hd]=INF;
    }

    Heriko hd>=k;
}

S main()
{
    Files();

    fr(n),fr(k);

    for(int i(1);i<=n;++i)
        fr(a[i]),mx=Hmax(mx,a[i]);

    double l(0),r(mx*1.0);

    while(l+(1e-4)<r)
    {
        double mid((l+r)/2.0);

        if(Check(mid))
            l=mid,ans=mid;
        else
            r=mid;
    }

    printf("%.4lf",ans);

    Heriko Deltana;
}

「C」

题目

「C」思路简述

首先把每个无向边改为两个有向边,然后在每个结点上,可以任意匹配来自两条不同树边的边。

总的方案数为 \(2^{n-1}\),但是还有重复的方案,减去即可。

「C」Code

CI MXX(1e6+1),MOD(1e9+7);

LL f[MXX],n,inv[MXX],fac[MXX],r[MXX],sum[MXX],tot[MXX],ans(1);

I LL FstPow(LL x,LL y)
{
    LL res(1);

    while(y)
    {
        if(y&1)
            (res*=x)%=MOD;
        
        (x*=x)%=MOD;
        y>>=1;
    }

    Heriko res;
}

S main()
{
    Files();

    fr(n);
    inv[0]=fac[0]=inv[1]=fac[1]=1;

    for(int i(2);i<=n;++i)
    {
        inv[i]=(MOD-MOD/i)*1ll*inv[MOD%i]%MOD;
        fac[i]=(fac[i-1]*1ll*i)%MOD;
    }
        
    for(int i(1);i<n;++i)
    {
        int x,y;
        fr(x),fr(y);
        ++r[x],++r[y];
    }

    f[0]=tot[0]=sum[0]=1;

    for(int i(1);i<=n;++i)
    {
        f[i]=(tot[i-1]+sum[i-1])%MOD;
        f[i]=(f[i]-f[i-1]+MOD)%MOD;
        f[i]=(1ll*f[i]*((MOD+1)>>1)%MOD*inv[i]%MOD);
        sum[i]=(sum[i-1]+f[i])%MOD;
        tot[i]=(tot[i-1]+sum[i])%MOD;
    }

    (ans*=FstPow(2,n-1))%=MOD;

    for(int i=1;i<=n;i++)
        ans=1ll*ans*fac[r[i]]%MOD*f[r[i]]%MOD;

    fw((ans+MOD)%MOD,1);

    Heriko Deltana;
}
posted @ 2021-10-27 15:18  HerikoDeltana  阅读(119)  评论(2编辑  收藏  举报