[USACO 2017 Dec Gold] Tutorial
Link:
A:
为了保证复杂度明显是从终结点往回退
结果一开始全在想优化建边$dfs$……其实可以不用建边直接$multiset$找可行边跑$bfs$就行了
由于保证每个点只进队列一次、被搜索到一次,因此复杂度为$O(n*log(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 MAXN=2e5+10; queue<int> q; multiset<P>::iterator it; multiset<P> sa,sb; int n,d,dist[MAXN],a[MAXN],b[MAXN]; int main() { scanf("%d%d",&n,&d); for(int i=1;i<=2*n;i++) scanf("%d%d",&a[i],&b[i]),a[i]=-a[i],b[i]=-b[i],dist[i]=-1; for(int i=1;i<=n;i++) { if(b[i]==0) q.push(i),dist[i]=1; else sa.insert(P(b[i],i)); if(a[n+i]==0) q.push(n+i),dist[n+i]=1; else sb.insert(P(a[n+i],n+i)); } while(!q.empty()) { int t=q.front();q.pop(); if(t<=n) { while(true) { it=sb.lower_bound(P(a[t],0)); if(it==sb.end()||(*it).X-a[t]>d) break; dist[(*it).Y]=dist[t]+1; q.push((*it).Y);sb.erase(it); } } else { while(true) { it=sa.lower_bound(P(b[t],0)); if(it==sa.end()||(*it).X-b[t]>d) break; dist[(*it).Y]=dist[t]+1; q.push((*it).Y);sa.erase(it); } } } for(int i=1;i<=n;i++) printf("%d\n",dist[i]); return 0; }
B:
将无根树转化为有根树方便计数
明显树形$dp$,转移$dp[i][j]=\prod_{k\in son} dp[k][(j+1)mod3]+dp[k][(j+2)mod3]$
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10,MOD=1e9+7; struct edge{int nxt,to;}e[MAXN<<2]; int n,k,x,y,head[MAXN],tot;ll dp[MAXN][3]; void add_edge(int x,int y) {e[++tot].nxt=head[x];e[tot].to=y;head[x]=tot;} void dfs(int x,int anc) { for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==anc) continue; dfs(e[i].to,x); for(int j=0;j<3;j++) (dp[x][j]*=dp[e[i].to][(j+1)%3]+dp[e[i].to][(j+2)%3])%=MOD; } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add_edge(x,y),add_edge(y,x); for(int i=1;i<=n;i++) dp[i][0]=dp[i][1]=dp[i][2]=1; for(int i=1;i<=k;i++) scanf("%d%d",&x,&y),y--,dp[x][(y+1)%3]=dp[x][(y+2)%3]=0; dfs(1,0); printf("%lld",(dp[1][0]+dp[1][1]+dp[1][2])%MOD); return 0; }
C:
明显一个序列的和到达$m$后再进行扩展不会使答案更优
于是想到用$two$ $pointers$找到所有刚刚达到$m$的区间,并用$multiset$维护最大值
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10; multiset<int,greater<int> > mx;ll sum,m; int n,f[MAXN],s[MAXN],res=1<<30; int main() { scanf("%d%lld",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d",&f[i],&s[i]); int lst=0; for(int i=1;i<=n;i++) { while(sum<m&&lst<=n) sum+=f[++lst],mx.insert(s[lst]); if(lst>n) break; res=min(res,*mx.begin()); //multiset.erase(val)会删掉所有相同元素! //因此要用multiset.erase(it)来删除 sum-=f[i];mx.erase(mx.find(s[i])); } printf("%d",res); return 0; }
注意:
1、可能有重复值因此要用$multiset$而非$set$
2、$multiset.erase(val)$会删掉所有相同元素,因此要用$multiset.erase(it)$来删除
3、使用$two$ $pointers$时注意末尾只删除不增加的情况