[USACO 2018 Jan Gold] Tutorial
Posted newera
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[USACO 2018 Jan Gold] Tutorial相关的知识,希望对你有一定的参考价值。
Link:
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; }
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; }
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; }
以上是关于[USACO 2018 Jan Gold] Tutorial的主要内容,如果未能解决你的问题,请参考以下文章
POJ3659 [usaco2008jan_gold]电话网络
bzoj2581 [USACO 2012 Jan Gold] Cow RunAnd-Or Tree
[USACO 2018 Open Gold] Tutorial