A层验题

T1

傻逼性质题。考场上蒙了个性质拍过了。
证明考虑调整法。直接说结论:排序之后顺序取第一个,最后一个,第二个……一直取完即可。

/*稍微化简一下变成一个平方和和一组积的形式
五子棋大师yugyppah65
wc我蒙的结论居然对了 于是我们现在就只需要维护前缀上的答案
考虑我们现在有了前面的答案 怎么搞当前的答案
由于数组是交错杂居的 所以奇数位置上的贡献一定是x-a[i] 偶数位置上就是a[i]-x
我们只需要算出当前的数和之前的所有数的差值加入答案就可以
我们存数组的时候直接存个前缀和 就像之前那样的按位置正负加入
然后查询的时候通过前面的奇偶性可以找到有没有x的贡献 如果x是奇数就没有 如果是偶数就有
复杂度线性 结束*/
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
#define int long long
using namespace std;
int n,k,sum,ans,a[300010];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    int l=1,r=n;
    for(int i=1,x;i<=n;i++){
        if(i&1)x=-a[l++];
        else x=a[r--],ans+=x;
        ans+=sum;sum+=x;
        printf("%lld\n",ans);
    }
    return 0;
}

T2

没改。

T3

没改。

T4

首先,对于这道题的评价:建议贺。
如果你想贺的话,代码在结尾。

题解

首先根据题意可以手动列出一个方程来:

\[E(x)=\frac{a\sum_{i=grandfather_x}E(i)+b\sum_{i=father_x}E(i)+c\sum_{i=bro_x}E(i)+d\sum_{i=son_x}E(i)+e\sum_{i=grandson_x}E(i)+1}{cnt_x} \]

然后他一开始给你的那些标记点初始化为 \(0\)\(cnt_x\) 是所有距离 \(x\) 路径 \(\le 2\) 的点数,记得算上 \(x\) 自己。意义显然。
然后我们如果暴力消元的话是 \(O(n^3)\) 的可以获得 \(10\) 分的好成绩。
如果你手动稀疏矩阵消元的话可以多过一个 \(subtask\) 。但是我没写。
我们直接跳过到正解部分。如果一句话概括的话就是丧心病狂的手动消元。

首先我们现在这个方程有六个常数: \(a,b,c,d,e,f(f=1)\) 。考虑怎么减少常数的个数。
观察到所有叶子节点都没有 \(d,e\) ,而根节点没有 \(a,b,c\)
考虑消元:我们把某个叶子节点 \(x\) 的所有兄弟加起来可以得到:

\[\sum E(x)=a\sum_{i=grandfather_x}E(i)+b\sum_{i=father_x}E(i)+c\sum_{i=bro_x}E(i)+f \]

然后显然 \(\sum E(x)=\sum_{i=bro_x}E(i)\) 。所以我们再来解一下这个方程,就可以把 \(c\)\(a,b,f\) 表示,从而消掉 \(c\) 这一项。
补一下手动解出来的方程:

\[\sum E(x)=a\sum_{i=grandfather_x}E(i)+b\sum_{i=father_x}E(i)+c\sum_E(x)+f \]

\[(1-c)\sum E(x)=a\sum_{i=grandfather_x}E(i)+b\sum_{i=father_x}E(i)+f \]

\[\sum E(x)=\frac{a\sum_{i=grandfather_x}E(i)+b\sum_{i=father_x}E(i)+f}{1-c} \]

然后我们现在把这些只有 \(a,b,f\) 三个变量的方程一步一步往上代就可以把所有的方程消成只剩三个常数。而既然根节点没有 \(a,b\) ,那么只有一个变量可以直接解出来。最后用根节点解出来的结果一步步往下代,就可以解出所有的元。

/*高斯消元柿子麻烦的要死谁愿写谁写去
考虑一个正解 首先手动消元
然后经过一波激烈的手动消元之后变成了一个鬼都看不懂的东西*/
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
const int mod=998244353;
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*a*ans%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
struct node{
    int v,next;
}edge[200010];
int n,m,t,head[100010],a[100010],b[100010],c[100010],d[100010],ans[100010];
int size[100010],deg[100010],a1[100010],b1[100010],c1[100010],d1[100010];
bool v[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs1(int x,int fa,int ff){
    deg[x]=size[fa];//deg是距离不超过2的节点个数 size是儿子个数
    if(ff)deg[x]+=2;
    else if(fa)deg[x]++;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=fa)size[x]++;
    }
    int a2=0,b2=0,c2=1,d2=0;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=fa){
            dfs1(edge[i].v,x,fa);
            deg[x]+=size[edge[i].v]+1;//加上儿子的儿子个数再加一个儿子
            a2=(a2+a[edge[i].v])%mod;
            b2=(b2+b[edge[i].v])%mod;
            c2=(c2-c[edge[i].v]+mod)%mod;
            d2=(d2+d[edge[i].v])%mod;
            //计算儿子的贡献 即∑a,∑b,1-∑c,∑d
        }
    }
    int inv=qpow(deg[x],mod-2),inv2=qpow(c2,mod-2),ret=1;
    //inv为初始的常数项1/deg,inv2是我们上面方程的那个1-c ret是我们初始的那个柿子中∑E(x)的系数
    a[x]=b[x]=c[x]=inv;d[x]=1;
    //初始化abc和常数f
    a2=1ll*a2*inv2%mod;b2=1ll*b2*inv2%mod;d2=1ll*d2*inv2%mod;
    //同样的初始化 就按照解出来的方程
    //现在我们有了一个儿子的∑bro E(i) 可以用它们消掉儿子的系数
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=fa){
            a[edge[i].v]=(a[edge[i].v]+1ll*c[edge[i].v]*a2%mod)%mod;
            b[edge[i].v]=(b[edge[i].v]+1ll*c[edge[i].v]*b2%mod)%mod;
            d[edge[i].v]=(d[edge[i].v]+1ll*c[edge[i].v]*d2%mod)%mod;
            //c[edge[i].v]是原来儿子的所有bro那一项的权值 现在我们可以用其他两项表示这一项(就是我们的a2 b2 d2) 可以直接消掉
            c[edge[i].v]=0;//消掉儿子的c
            b[x]=(b[x]+1ll*a[edge[i].v]*(b1[edge[i].v]+1)%mod*inv%mod)%mod;
            ret=(ret-1ll*b[edge[i].v]*(b1[edge[i].v]+1)%mod*inv%mod+mod)%mod;
            //我们同时要消去d和e 这里所有带1的数组都是儿子的和 b是父亲 而a[edge[i].v]是儿子的祖父也就是父亲
            //ret是自己的系数所以用儿子的父亲b更新
            //直接考虑儿子的贡献就不需要什么d和e了 因为de的初始值是inv
            //为什么要+1我不知道 如果有知道的请留下评论
            d[x]=(d[x]+1ll*d[edge[i].v]*(b1[edge[i].v]+1)%mod*inv%mod)%mod;
            d[x]=(d[x]+1ll*d1[edge[i].v]*inv%mod)%mod;
            ret=(ret-1ll*a1[edge[i].v]*inv%mod+mod)%mod;
            //常数是要整个子树都加上来的
            a1[x]=(a1[x]+a[edge[i].v])%mod;
            b1[x]=(b1[x]+b[edge[i].v])%mod;
            d1[x]=(d1[x]+d[edge[i].v])%mod;
            //统计儿子的和
        }
    }
    inv=qpow(ret,mod-2);
    a[x]=1ll*a[x]*inv%mod;b[x]=1ll*b[x]*inv%mod;
    c[x]=1ll*c[x]*inv%mod;d[x]=1ll*d[x]*inv%mod;
    //这步显然 直接把∑E(x)的系数化为1 也保证了上面直接搞系数的正确性
    if(v[x]){
        a[x]=b[x]=c[x]=d[x]=0;//如果被标记显然都是0
    }
}
void dfs2(int x,int fa,int ff){
    ans[x]=(d[x]+1ll*ans[fa]*b[x]%mod+1ll*ans[ff]*a[x]%mod)%mod;//代入求解即可
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=fa)dfs2(edge[i].v,x,fa);
    }
}
int main(){
    scanf("%d%d",&n,&m);size[0]=1;
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=m;i++){
        int x;scanf("%d",&x);v[x]=true;
    }
    dfs1(1,0,0);
    int inv=qpow((1-c[1]+mod)%mod,mod-2);
    a[1]=1ll*a[1]*inv%mod;b[1]=1ll*b[1]*inv%mod;d[1]=1ll*d[1]*inv%mod;c[1]=0;
    dfs2(1,0,0);
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
}
posted @ 2022-09-25 19:00  gtm1514  阅读(31)  评论(0编辑  收藏  举报