The Preliminary Contest for ICPC Asia Xuzhou 2019 I J

I. query

题意:给出n的一个排列,有m个询问[l,r],询问[l,r]直接有倍数关系的pair个数。

解法:比赛完之后听说是原题,但是我没做过呀,做题太少了qwq。首先因为数字是1-n的,所以所有的pair总数就是n/1+n/2+n/3...=nlogn。也就是说我们可以把所有的pair预处理出来,然后对于一个pair<x,y>,我们令sum[y]++,做完之后我们对sum做个前缀和,那么我们最后得到的sum是什么呢?sum[i]就代表左右端点都小于等于i的pair数量。那么此时对于一个询问[li,ri],ans[i]=sum[ri]-sum[li]。但是还不能高兴太早,注意到其实这一部分ans还加多了横跨查询左端点li的数量,我们需要减去这部分数量。

现在我们就要解决这个问题:从左到右扫描i,然后在Bit上记录[1,i-1]的数的所有pair的右端点(例如有一对pair<x,y>,那么就令bit[y]++),那么此时对于一i为左端点的查询[l,r],要减去的部分就是bit[r]-bit[l-1]。为什么?因为我们此时我们只遍历到i且只把[1,i-1]的所有数的pair(即这些pair的左端点只在[1,i-1]区间)都计算右端点贡献了,那么bit[y]就是这堆[1,i-1]的pair的右端点总和小于等于y的pair数,bit[x-1]同理,那么bit[y]-bit[x-1]就是这些pair的右端点只在[i,y]区间且根据上面他们的左端点只在[1,i-1]区间,当然这个数就是左端点在[1,x-1]右端点在[x,y]的pair数了。

于是此题就可以AC了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,a[N],pos[N],ans[N],x[N],y[N],sum[N];
vector<int> G[N],v[N];

int bit[N];
void update(int x,int v) {
    for (;x<=n;x+=x&-x) bit[x]+=v;
}
int query(int x) {
    int ret=0;
    for (;x;x-=x&-x) ret+=bit[x];
    return ret;
}

int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) {
        scanf("%d%d",&x[i],&y[i]);
        G[x[i]].push_back(i);
    }
    
    for (int i=1;i<=n;i++) pos[a[i]]=i;
    for (int i=1;i<=n;i++)
        for (int j=2*i;j<=n;j+=i) {
            int t1=pos[i],t2=pos[j];
            if (t1>t2) swap(t1,t2);
            v[t1].push_back(t2);
            sum[t2]++;
        }
    for (int i=1;i<=n;i++) sum[i]+=sum[i-1];    
    for (int i=1;i<=n;i++) {
        for (int j=0;j<G[i].size();j++)
            ans[G[i][j]]-=query(y[G[i][j]])-query(x[G[i][j]]-1);
        for (int j=0;j<v[i].size();j++)
            update(v[i][j],1);    
    }
    for (int i=1;i<=m;i++)
        ans[i]+=sum[y[i]]-sum[x[i]-1];
    for (int i=1;i<=m;i++) printf("%d
",ans[i]);    
    return 0;
}
View Code

J. Random Access Iterator

题意:给出一棵n个结点的树,从树根1出发,对于每个点都是选择k次:每次选择一个儿子走直到没得走(k是该点儿子个数)。问从1出发能到达最深叶子的概率。

解法:裸的树形概率dp。设dp[x]代表从x出发子树能到达最深叶子概率。

那么dp[x]=1 - ( sigma( (1-dp[y]) / k) ) ^ k 。其实就是反向思考,每个儿子都不能走到最深的概率相加就是一次实验不能到达最深叶子的概率,那么k次方就是k次实验都不能到达最深叶子概率,然后用1减就是,至少到达一次的概率。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int P=1e9+7;
typedef long long LL;
int n,indeg[N],son[N],Max,dep[N];
vector<int> G[N];
LL dp[N];

LL power(LL x,LL p) {
    if (x==0) return 0;
    LL ret=1;
    for (;p;p>>=1) {
        if (p&1) ret=ret*x%P;
        x=x*x%P;
    }
    return ret;
}

queue<int> q;
void bfs() {
    q.push(1); dep[1]=1;
    while (!q.empty()) {
        int x=q.front(); q.pop();
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (dep[y]==0) {
                dep[y]=dep[x]+1;
                Max=max(Max,dep[y]);
                q.push(y);
                indeg[x]++; son[x]++;
            }
        }
    }
}

void toposort() {
    for (int i=1;i<=n;i++) {
        dp[i]=0;
        if (indeg[i]==0) {
            q.push(i);
            if (dep[i]!=Max) dp[i]=1;
        }
    }
        
    while (!q.empty()) {
        int x=q.front(); q.pop();
        dp[x]=power(dp[x],son[x]);
        dp[x]=(1-dp[x]+P)%P;
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (indeg[y]==0) continue;
            dp[y]=(dp[y]+(1-dp[x]+P)%P*power(son[y],P-2)%P)%P;
            if (--indeg[y]==0) q.push(y);
        }
    }    
}

int main()
{
    cin>>n;
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    bfs();
    toposort();
    printf("%lld",dp[1]);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/clno1/p/11483368.html