Link:
A:
$dp[i][j][k]$表示前$i$个中有$j$个0且末位为$k$的最优解
状态数$O(n^3)$
#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=105,INF=1<<30; int n,dat[MAXN],dp[MAXN][MAXN][MAXN]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&dat[i]); for(int i=0;i<MAXN;i++) for(int j=0;j<MAXN;j++) for(int k=0;k<MAXN;k++) dp[i][j][k]=INF; if(dat[1]!=0) dp[1][1][0]=1; else dp[1][1][0]=0; for(int i=1;i<n;i++) for(int j=1;j<=i;j++) for(int k=0;k<=i-j;k++) if(dp[i][j][k]!=INF) { dp[i+1][j][k+1]=min(dp[i+1][j][k+1],dp[i][j][k]+(dat[i+1]!=k+1)); dp[i+1][j+1][0]=min(dp[i+1][j+1][0],dp[i][j][k]+(dat[i+1]!=0)); } for(int i=1;i<=n;i++) { int res=INF; for(int j=0;j<=n;j++) res=min(res,dp[n][i][j]); printf("%d\n",res); } return 0; }
B:
对于每一个节点分别计算其上方和下方的答案,其中下方答案明显可以一遍$dfs$
上方答案我一开始是$O(树高)$求的,明显会被卡……
其实可以再做一遍$dfs$,用父节点除去该棵子树的贡献再加上走到父节点的贡献即可
注意特殊处理叶子结点
#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; char s[MAXN][20]; struct edge{int nxt,to;}e[MAXN<<2]; int n,x,head[MAXN],len[MAXN],f[MAXN],num[MAXN],tot; ll sum[MAXN],cnt[MAXN],tmp,res[MAXN],mn=1ll<<60,lf; void add_edge(int x,int y) { e[++tot]={head[x],y};head[x]=tot; e[++tot]={head[y],x};head[y]=tot; } void dfs1(int x,int anc) { for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==anc) continue; f[e[i].to]=x;dfs1(e[i].to,x); cnt[x]+=cnt[e[i].to]; sum[x]+=sum[e[i].to]+cnt[e[i].to]*(len[e[i].to]+1); } } void dfs2(int x,int anc) { for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==anc||!num[e[i].to]) continue; res[e[i].to]=sum[e[i].to]; res[e[i].to]+=(res[x]-sum[e[i].to]-cnt[e[i].to]*(len[e[i].to]+1)); res[e[i].to]+=3*(lf-cnt[e[i].to]);mn=min(mn,res[e[i].to]); dfs2(e[i].to,x); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s%d",s[i]+1,&num[i]); len[i]=strlen(s[i]+1); for(int j=1;j<=num[i];j++) scanf("%d",&x),add_edge(x,i); if(!num[i]) cnt[i]=1,len[i]--,lf++; } dfs1(1,0); res[1]=mn=sum[1]; dfs2(1,0); printf("%lld",mn); return 0; }
C:
好像和前面一题差不多?
可以离线从小到大加点用$set$和$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; set<int> s;set<int>::iterator it; int n,m,res[MAXN];P sw[MAXN]; struct Query{int s,d,id;}bt[MAXN]; bool cmp(Query a,Query b){return a.s<b.s;} int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&sw[i].X),sw[i].Y=i; for(int i=1;i<=m;i++) scanf("%d%d",&bt[i].s,&bt[i].d),bt[i].id=i; sort(sw+1,sw+n+1);sort(bt+1,bt+m+1,cmp); int lst=1,pre,nxt; s.insert(1);s.insert(n);mx.insert(n-1); for(int i=1;i<=n;i++) { while(sw[lst].X<=bt[i].s&&lst<=n) { if(sw[lst].Y!=1&&sw[lst].Y!=n) { it=s.lower_bound(sw[lst].Y); nxt=*it;pre=*(--it); mx.erase(mx.find(nxt-pre)); mx.insert(nxt-sw[lst].Y);mx.insert(sw[lst].Y-pre); } s.insert(sw[lst].Y);lst++; } res[bt[i].id]=(*mx.begin()<=bt[i].d); } for(int i=1;i<=m;i++) printf("%d\n",res[i]); return 0; }
如果将问题转化为在原序列中大量删除的,手写链表即可