1. 程式人生 > >[USACO 2018 Jan Gold] Tutorial

[USACO 2018 Jan Gold] Tutorial

|| hide max a* pri targe turn its lse

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\n",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

[USACO 2018 Jan Gold] Tutorial