• [USACO 2018 Jan Gold] Tutorial


    Link:

    USACO 2018 Jan Gold 传送门

    A:

    对于不同的$k$,发现限制就是小于$k$的边不能走

    那么此时的答案就是由大于等于$k$的边形成的图中$v$所在的连通块除去$v$的大小

    为了优化建图过程,考虑离线,将询问和边都按权值从大到小排序,依次加边即可

    维护连通性和连通块大小用并查集

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    typedef long long ll;
    typedef pair<int,int> P;
    typedef double db;
    const int MAXN=1e5+10;
    struct query{int k,v,id;}qry[MAXN];
    struct edge{int x,y,r;}e[MAXN<<2];
    int n,q,f[MAXN],sz[MAXN],res[MAXN];
    
    bool cmp1(edge a,edge b){return a.r>b.r;}
    bool cmp2(query a,query b){return a.k>b.k;}
    
    int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
    void merge(int x,int y)
    {
        int px=find(x),py=find(y);
        if(px==py) return;
        f[px]=py;sz[py]+=sz[px];
    }
    
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++) 
            f[i]=i,sz[i]=1;
        for(int i=1;i<n;i++)
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].r);
        for(int i=1;i<=q;i++)
            scanf("%d%d",&qry[i].k,&qry[i].v),qry[i].id=i;
        sort(e+1,e+n,cmp1);sort(qry+1,qry+q+1,cmp2);    
        
        int top=1;
        for(int i=1;i<=q;i++)
        {
            while(e[top].r>=qry[i].k&&top<n)
                merge(e[top].x,e[top].y),top++;
            
            res[qry[i].id]=sz[find(qry[i].v)]-1;
        }
        for(int i=1;i<=q;i++)
            printf("%d
    ",res[i]);
        return 0;
    }
    Problem A

    B:

    将出发点$k$作为根

    维护每个点到最近叶子的距离$dist$和该子树内需要的叶子$sum$

    如果$dist<dep$明显可以只用一个叶子,$sum=1$就好了

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    typedef long long ll;
    typedef pair<int,int> P;
    typedef double db;
    const int MAXN=1e5+10;
    struct edge{int nxt,to;}e[MAXN<<2];
    int n,k,x,y,head[MAXN],sum[MAXN],mn[MAXN],dep[MAXN],tot;
    
    void add_edge(int x,int y)
    {
        e[++tot].nxt=head[x];e[tot].to=y;head[x]=tot;
        e[++tot].nxt=head[y];e[tot].to=x;head[y]=tot;
    }
    
    void dfs(int x,int anc)
    {
        int cnt=0;
        for(int i=head[x];i;i=e[i].nxt)
        {
            if(e[i].to==anc) continue;
            dep[e[i].to]=dep[x]+1;
            cnt++;dfs(e[i].to,x);
            sum[x]+=sum[e[i].to];
            
            if(!mn[x]) mn[x]=mn[e[i].to]+1;
            else mn[x]=min(mn[x],mn[e[i].to]+1);
        }
        
        if(!cnt||dep[x]>=mn[x]) sum[x]=1;
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<n;i++)
            scanf("%d%d",&x,&y),add_edge(x,y);
        
        dfs(k,0);
        printf("%d",sum[k]);
        return 0;
    }
    Problem B

    C:

    关于最终状态唯一能得到的性质为:一定会有连续至少$k$个相同的一段

    结果我一开始全在想如何将可行方案去重……但明显补集更好求啊!

    令$dp[i]$表示前$i$位连续最多$k-1$个相同的方案数

    $dp[i]=(m-1)*sum_{j=1}^{k-1} dp[i-j]$

    用$suf$时刻保存$dp$数组的后缀和就能$O(n)$求出$dp[n]$了

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    typedef long long ll;
    typedef pair<int,int> P;
    typedef double db;
    const int MOD=1e9+7,MAXN=1e6+10;
    ll n,m,k,suf,dp[MAXN];
    
    ll quick_pow(ll a,ll b)
    {
        ll ret=1;
        for(;b;b>>=1,a=a*a%MOD)
            if(b&1) ret=ret*a%MOD;
        return ret;
    }
    
    int main()
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=1;i<=k-1;i++)
            dp[i]=quick_pow(m,i),(suf+=dp[i])%=MOD;
        for(int i=k;i<=n;i++)
            dp[i]=suf*(m-1)%MOD,(suf+=dp[i]-dp[i-k+1]+MOD)%=MOD;
        
        printf("%lld",(quick_pow(m,n)-dp[n]+MOD)%MOD);
        return 0;
    }
    Problem C
  • 相关阅读:
    UILabel 分段改变文字颜色和字体
    ios tableView删除行
    ios 控件点击没反应的问题
    ios MJRefresh最新的一些使用
    android下水波纹效果实现
    9patch的用法,简单两句就会用了。
    有关android.support.v7.app.ActionBarActivity错误的问题
    android中fragment的使用及与activity之间的通信
    [转]CSS中继承性属性和非继承性的属性
    js中的attribute详解
  • 原文地址:https://www.cnblogs.com/newera/p/9583338.html
Copyright © 2020-2023  润新知