7月13日考试 题解(DFS序+期望+线段树优化建图)

T1 sign

题目大意:给出一棵 N 个节点的树,求所有起点为叶节点的有向路径,其 上每一条边权值和的和。N<=10000

水题。考试的时候毒瘤出题人(学长orz)把读入顺序改了一下,于是很多人爆零(包括我QAQ。

先dfs序把以$i$为根的子树大小$size[i]$和所含叶子结点个数$s[i]$求出。考虑每条边对答案的贡献。

  1.子树里的叶子结点往外走,这一部分的贡献为$s[i]*(n-size[i])*dis$

  2.子树外的叶子结点往里走,这一部分的贡献为$(sum-s[i])*size[i]*dis$,$sum$指叶子结点个数。

然后枚举边累加就好。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,ans,du[100005],size[100005],sum,root,ss[100005],tot;
int head[200005],cnt;
struct node
{
    int next,to,dis;
}edge[200005];
struct edge
{
    int from,to,dis;
}a[100005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void add(int from,int to,int dis)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    edge[cnt].dis=dis;
    head[from]=cnt;
}
inline void dfs(int now,int fa)
{
    size[now]=1;
    if (du[now]==1) ss[now]=1;
    for (int i=head[now];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if (to==fa) continue;
        a[++tot].from=now,a[tot].to=to,a[tot].dis=edge[i].dis;
        dfs(to,now);
        size[now]+=size[to];
        ss[now]+=ss[to];
    }
}
signed main()
{
    n=read();
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read(),z=read();
        add(y,z,x);add(z,y,x);du[z]++;du[y]++;
    }
    for (int i=1;i<=n;i++)
    {
        if (du[i]>1) root=i;
        else sum++;
    }
    dfs(root,0);
    for (int i=1;i<n;i++) 
        ans+=ss[a[i].to]*(n-size[a[i].to])*a[i].dis+size[a[i].to]*(sum-ss[a[i].to])*a[i].dis;
    printf("%lld",ans);
    return 0;
}

T2 map

题目大意:给定一张含有$n$个点的无向完全图,从$1$号点出发,每秒随机走一条边。$q$次询问,每次询问$t_i$秒时在点$1$的概率。

对于60%的数据,$n,q,t\leq 10^5$

对于100%的数据,$n,q,t\leq 10^{18}$

60分的很好想。设$f[i]$表示第$i$秒时在点$1$的概率,$g[i]$表示第$i$秒时不在点$1$的概率。易得到:

$f[i]=g[i-1]*\frac{1}{n-1}*(n-1)=g[i-1]$

$g[i]=f[i-1]*\frac{1}{n-1}+g[i-1]*\frac{n-2}{n-1}$

然后考试的时候就想到这里……60pts。正解只需要再往下推一步。

变换一下形式:$g[i]=g[i-2]*\frac{1}{n-1}+g[i-1]*\frac{n-2}{n-1}$

移项,得到:$g[i]-g[i-1]=-\frac{1}{n-1}*(g[i-1]-g[i-2])$

然后就是等比数列化简,得到通项公式:$g[i]=\frac{(n-1)^i-(-1)^i}{n*(n-1)^{i-1}},f[i]=\frac{(n-1)^{i-1}-(-1)^i}{n*(n-1)^{i-1}}$

最后快速幂求逆元就好。注意要$n$要模$mod$。时间复杂度$O(q\log n)$。

代码:

#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int mod=998244353;
int n,t,q;
inline int qpow(int x,int y)
{
    int res=1;x%=mod;
    while(y>0){
        if (y%2==1) res=res*x%mod;res%=mod;
        x=x*x%mod;
        y>>=1;
    }
    return res%mod;
}
signed main()
{
    scanf("%lld%lld",&n,&q);n%=mod;
    while(q--)
    {
        scanf("%lld",&t);
        if (t==0){
            printf("1\n");
            continue;
        }
        int x=qpow(n-1,t-1)%mod;
        if (t&1) printf("%lld\n",((x-1)%mod*qpow(n*x,mod-2)%mod)%mod);
        else printf("%lld\n",((x+1)%mod*qpow(x*n,mod-2)%mod)%mod);
    }
    return 0;
}

T3 【PA2011】Journeys

题目链接

题目大意:点从$1-n$标号。给定$[l1,r1]$和$[l2,r2]$,表示$[l1,r1]$内的点与$[l2,r2]$内任意一点都有长度为$1$的边。求点$s$的单源最短路径。

线段树优化建图模板题。

建立一棵入树,一棵出树,对于每次连边建两个虚点,在其间连一条权为1的边;然后从出树连出来,连进去入树,边权均为0。注意边是双向的,因此需要做两遍。

最短路不需要 Dijkstra,只需要 01BFS。时间复杂度$O(n\log n)$。

关于各种图的优化技巧可以看大佬的博客orz

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,S,head[8000005],cnt,tot,dis[4000005],ls[4000005],rs[4000005];
struct edge
{
    int next,to,dis;
}edge[8000005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void addedge(int from,int to,int dis)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    edge[cnt].dis=dis;
    head[from]=cnt;
}
void Build(int &p,int &q,int l,int r) {
    if(l==r){
        p=l,q=l;
        return ;
    }
    if(!p)p=++tot;
    if(!q)q=++tot;
    int mid=(l+r)/2;
    Build(ls[p],ls[q],l,mid),addedge(ls[p],p,0),addedge(q,ls[q],0);
    Build(rs[p],rs[q],mid+1,r),addedge(rs[p],p,0),addedge(q,rs[q],0);
}
void Add(int p,int l,int r,int x,int y,int z,int flag) {
    if(x<=l&&r<=y) {
        if(flag)addedge(z,p,0);
        else addedge(p,z,0);
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)Add(ls[p],l,mid,x,y,z,flag);
    if(mid<y)Add(rs[p],mid+1,r,x,y,z,flag);
}
void Dijkstra() {
    memset(dis,0x3f,sizeof(dis));
    deque<int> q;
    dis[S]=0;
    q.push_back(S);
    while(!q.empty()) {
        int now=q.front();
        q.pop_front();
        for(int i=head[now]; i; i=edge[i].next) {
            int y=edge[i].to,v=edge[i].dis;
            if(dis[y]>dis[now]+v) {
                dis[y]=dis[now]+v;
                if(v)q.push_back(y);
                else q.push_front(y);
            }
        }
    }
    for(int i=1;i<=n;i++)printf("%d\n",dis[i]);
}
int main()
{
    n=read(),m=read(),S=read(),tot=n;
    int root1=0,root2=0;
    Build(root1,root2,1,n);
    while(m--){
        int x=read(),y=read(),z=read(),w=read(),a=++tot,b=++tot;
        addedge(a,b,1);
        Add(root1,1,n,x,y,a,0);
        Add(root2,1,n,z,w,b,1);
        a=++tot,b=++tot;
        addedge(a,b,1);
        Add(root1,1,n,z,w,a,0);
        Add(root2,1,n,x,y,b,1);
    }    
    Dijkstra();
    return 0;
}

 

posted @ 2020-07-13 18:57  我亦如此向往  阅读(190)  评论(0编辑  收藏  举报