NOIP2018普及&提高题解
第一次考$NOIP$的我已经自闭了
$CCF$告诉我们了一件事情,要对自己写的程序有信仰,相信$CCF$的数据是水的
普及组:
分数:$100+100+30+100=330$
$1.titile$:
$cin$,$scanf$都试了试,却没有$A$掉第二个样例,最后$getchar()$$5$次$A$掉了
考场上加了一句读掉换行就停止,要是不加就会$WA$的很惨
$5$次$getchar()$,遇见换行停止
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int ans=0,tot; int main(){ freopen("title.in","r",stdin); freopen("title.out","w",stdout); while(tot<=5){ char str=getchar(); if(int(str)==10) break; if((str>='0'&&str<='9')||(str>='a'&&str<='z')||(str>='A'&&str<='Z')) ans++; tot++; } cout<<ans; return 0; }
$2.fight$:
自闭了,被卡精度,简单的$O(n)$暴力。
$update$:$CCF$的数据是真水啊,好像开$int$过掉了
直接暴力枚举加入之后的势力就行
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int m,p1,s1,s2,pos,n,a[100011],sum1,sum2,minn,inf=2<<30-1; signed main(){ freopen("fight.in","r",stdin); freopen("fight.out","w",stdout); n=read(); for(int i=1;i<=n;i++) a[i]=read(); m=read(),p1=read(),s1=read(),s2=read(); a[p1]+=s1; for(int i=1;i<m;i++) sum1+=(m-i)*a[i]; for(int i=m+1;i<=n;i++) sum2+=(i-m)*a[i]; minn=abs(sum2-sum1); pos=m; for(int i=1;i<=n;i++){ int ans1=sum1,ans2=sum2; if(i<m) ans1+=(m-i)*s2; if(i>m) ans2+=(i-m)*s2; int k=abs(ans1-ans2); if(k<minn){minn=k,pos=i;} } cout<<pos; return 0; }
$3.bus$:
观察$n$与$m$的范围,应该是一个二维$dp$,所以我们令$dp(i,j)$表示时间为$a[i]+j$,第$i$个人上车的等候总和,所以可以分两种情况考虑
1.当第$i$个人与第$i-1$个人不坐同一辆车时,则$dp(i,j)=max(dp(i,j),dp(i-1,k)+j)$
2.当第$i$个人愿意去并且与第$i-1$个人坐同一辆车则$dp(i,j)=max(dp(i,j),dp(i-1,k)+j)$
主要是对两种情况的判断
详情见代码
考场上其实写的是$dp(i,j)$为第$i$个人,当前时间为$j$且第$i$个人上车的等车情况,最后挂了,时间巨大
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=501; const int MAXM=201; int N,M,A[MAXN],f[MAXN][MAXM],INF=INT_MAX; int main(){ freopen("bus.in","r",stdin); freopen("bus.out","w",stdout); memset(f,127/3,sizeof(f)); N=read(),M=read();for(int i=1;i<=N;i++) A[i]=read();sort(A+1,A+N+1); for(int i=0;i<M;i++) f[1][i]=i; for(int i=2;i<=N;i++){ for(int j=0;j<2*M;j++){ if(j+A[i]-A[i-1]>=0&&j+A[i]-A[i-1]<2*M) f[i][j]=f[i-1][j+A[i]-A[i-1]]+j; for(int p=0;p<2*M;p++){ int T=j+A[i],t1=p+A[i-1]+M;if(t1>T) continue; f[i][j]=min(f[i][j],f[i-1][p]+j); } } } int Minn=INF; for(int i=0;i<2*M;i++) Minn=min(Minn,f[N][i]); printf("%d\n",Minn);return 0; }
$4.tree$:
搜索。
因为树是满足条件的必须
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1000001; struct node{ int siz,id; }f[MAXN]; int L[MAXN],R[MAXN],siz[MAXN],N,A[MAXN],d[MAXN],rt; void dfs1(int u){ siz[u]=1; if(L[u]) dfs1(L[u]),siz[u]+=siz[L[u]]; if(R[u]) dfs1(R[u]),siz[u]+=siz[R[u]]; return; } bool cmp(node x1,node x2){return x1.siz>x2.siz;} bool checker(int u,int v){ if(!u&&!v) return 1; if(siz[u]!=siz[v]) return 0; if(A[u]!=A[v]) return 0; return checker(L[u],R[v])&&checker(R[u],L[v]); } int main(){ N=read(); for(int i=1;i<=N;i++) A[i]=read(); for(int i=1;i<=N;i++){ L[i]=read(),R[i]=read(); if(L[i]==-1) L[i]=0;if(R[i]==-1) R[i]=0; d[L[i]]++,d[R[i]]++; } for(int i=1;i<=N;i++) if(!d[i]){rt=i;break;}dfs1(rt); for(int i=1;i<=N;i++) f[i].siz=siz[i],f[i].id=i;sort(f+1,f+N+1,cmp); for(int i=1;i<=N;i++){ int tr=f[i].id; if(checker(L[tr],R[tr])){printf("%d\n",f[i].siz);return 0;} }return 0; }
提高组:
分数:
day1:$100+0+55=155$
day2:$100+60+44=204$
$day1+day2=359$
$1.road$
差分即可,将序列差分以后可以看成将一个位置 $+1$ ,一个位置 $-1$,直接统计 $+1$ 块即可,因为可以将 $+1$ 块与 $-1$ 块匹配。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int cnt=1; struct node{ int l,r; }x[110001]; int n,a[110001],sum; signed main(){ freopen("road.in","r",stdin); freopen("road.out","w",stdout); n=read(); for(int i=1;i<=n;i++) a[i]=read(); x[1].l=1; for(int i=2;i<=n;i++){ if(a[i]>a[i-1]) x[cnt].r=i-1,x[++cnt].l=i; } int minn=0; x[cnt].r=n; for(int i=1;i<=cnt;i++){ sum+=(a[x[i].l]-minn); minn=a[x[i].r]; } cout<<sum; return 0; }
$2.money$
考虑$n=2$的情况入手,发现若只能取一个就必须一个是另一个的倍数,然后我们就可以大胆猜想答案的货币系统一定是其原先货币系统的子集,然后经过简单思考发现这个结论是对的。
然后就是一个十分水的背包$dp$了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return ans*f; } int T,a[10001],n,ans,M; bool dp[1000001]; int main() { freopen("money.in","r",stdin); freopen("money.out","w",stdout); T=read(); while(T--){ n=read(); ans=0; for(int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+n+1); M=a[n]; memset(dp,0,sizeof(dp)); dp[0]=1; for(int i=1;i<=n;i++){ if(!dp[a[i]]){ ans++; for(int j=a[i];j<=M;j++) dp[j]|=dp[j-a[i]]; } } cout<<ans<<endl; } }
$3.track$
主要的思想就是选择两个子孙链上的进行合并,然后在不能合的里面选择最大的一条往上传即可。正确性显然。$multiset$ 维护一下即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<set> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=50001; multiset<int> s[MAXN]; int n,k; struct node{ int u,v,w,nex; }x[MAXN<<1]; int ans,l,r,head[MAXN],cnt,maxn; void add(int u,int v,int w){ x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++; } int dfs(int xx,int fath,int lim){ s[xx].clear(); for(int i=head[xx];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; int val=dfs(x[i].v,xx,lim)+x[i].w; if(val>=lim) ans++; else s[xx].insert(val); } int maxn=0; while(!s[xx].empty()){ if(s[xx].size()==1) return max(maxn,*s[xx].begin()); multiset<int> :: iterator it=s[xx].lower_bound(lim-*s[xx].begin()); if(it==s[xx].begin()&&s[xx].count(*it)==1) it++; if(it==s[xx].end()){ maxn=max(maxn,*s[xx].begin()); s[xx].erase(s[xx].find(*s[xx].begin())); }else{ ans++; s[xx].erase(s[xx].find(*s[xx].begin())); s[xx].erase(s[xx].find(*it)); } } return maxn; } bool check(int xx){ ans=0; dfs(1,0,xx); return ans>=k; } int dis[MAXN]; void dfs1(int f,int fath){ for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; dis[x[i].v]=dis[f]+x[i].w; dfs1(x[i].v,f); } return; } int get_dis(){ dfs1(1,0); int maxn=0,pos; for(int i=1;i<=n;i++){ if(dis[i]>maxn){ maxn=dis[i],pos=i; } } dfs1(pos,0); maxn=0; for(int i=1;i<=n;i++) maxn=max(maxn,dis[i]); return maxn; } signed main(){ freopen("track.in","r",stdin); freopen("track.out","w",stdout); memset(head,-1,sizeof(head)); n=read(),k=read(); for(int i=1;i<n;i++){ int u=read(),v=read(),w=read(); add(u,v,w),add(v,u,w); } r=get_dis(); while(l<=r){ int mid=l+r>>1; if(check(mid)) l=mid+1,maxn=max(maxn,mid); else r=mid-1; } cout<<maxn; }
$4.travel$
$update$:$CCF$的数据是真水啊,好像$O(n^2\times m)$过掉了
树的做法很简单吧,当遍历到节点$i$时,继续$dfs$编号最小且此节点的父亲是$i$的,$dfs$是因为与题意相符,回溯与往下。
当是个基环树的时候,我们可以发现其实有一条边是没有做过的,就去每次暴力删边,然后将每次的字典序记录下来,最后输出最小字典序即可
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct node{ int u,v,nex; }x[10001]; int head[5001]; int cnt,n,m; void add(int u,int v){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } int st[5001]; bool vis[5001]; int delu,delv,tot; int da[5001]; int pig; int kk[5001][5001]; int scc,cntcnt[5001],pos; void dfs(int f,int fath){ if(pig==1) return; int son=0; int sry=0; while(sry<cntcnt[f]){ ++sry; pos=kk[f][sry]; if(vis[pos]) continue; if((f==delu&&pos==delv)||(f==delv&&pos==delu)) continue; vis[pos]=1; st[++st[0]]=pos; if(st[st[0]]>da[st[0]]&&pig!=2){pig=1;return;} if(st[st[0]]<da[st[0]]){pig=2;} dfs(pos,f); } return; } struct node1{ int u,v; }se[10001]; int main(){ freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); memset(da,127/3,sizeof(da)); memset(head,-1,sizeof(head)); n=read(),m=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(); add(u,v),add(v,u); se[i].u=u,se[i].v=v; } for(int i=1;i<=n;i++){ for(int j=head[i];j!=-1;j=x[j].nex){ kk[i][++cntcnt[i]]=x[j].v; } sort(kk[i]+1,kk[i]+cntcnt[i]+1); } delu=-1,delv=-1; if(m==n-1){ st[0]=1; st[st[0]]=1; vis[1]=1; dfs(1,0); for(int i=1;i<=st[0];i++) cout<<st[i]<<" "; return 0; } else{ for(int i=1;i<=m;i++){ pig=0; memset(vis,0,sizeof(vis)); delu=se[i].u,delv=se[i].v; st[0]=1; st[1]=1; vis[1]=1; dfs(1,0); if(st[0]!=n) continue; bool ff=1; for(int j=1;j<=n;j++){ if(st[j]>da[j]){ff=0;break;} if(st[j]<da[j]){ff=1;break;} } if(ff) for(int j=1;j<=n;j++) da[j]=st[j]; } for(int j=1;j<=n;j++) printf("%d ",da[j]); return 0; } }
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct node{ int u,v,nex; }x[20001]; int head[10001]; int cnt,n,m,inf=99999999; void add(int u,int v){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } int st[10001],vis[10001],delu,delv,tot; int da[10001]; int pig; void dfs(int f,int fath){ if(pig==1) return; int son=0; for(int i=head[f];i!=-1;i=x[i].nex){ if((delu==f&&delv==x[i].v)||(delu==x[i].v&&delv==f)) continue; if(x[i].v==fath) continue; if(vis[x[i].v]) continue; if((delu==f&&delv==x[i].v)||(delu==x[i].v&&delv==f)) continue; if(x[i].v==fath) continue; if(vis[x[i].v]) continue; son++; } while(son!=0){ int minn=inf,pos=-1; for(int i=head[f];i!=-1;i=x[i].nex){ if((delu==f&&delv==x[i].v)||(delu==x[i].v&&delv==f)) continue; if(x[i].v==fath) continue; if(vis[x[i].v]) continue; if(x[i].v<minn){ minn=x[i].v; pos=x[i].v; } } if(pos==-1) break; vis[pos]=1; st[++st[0]]=pos; if(st[st[0]]>da[st[0]]&&pig!=100){pig=1;return;} if(st[st[0]]<da[st[0]]){pig=100;} dfs(pos,f); son--; } return; } struct node1{ int u,v; }se[10001]; int main(){ freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); memset(da,127,sizeof(da)); memset(head,-1,sizeof(head)); n=read(),m=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(); add(u,v),add(v,u); se[i].u=u,se[i].v=v; } delu=-1,delv=-1; if(m==n-1){ st[0]=1; st[st[0]]=1; dfs(1,0); for(int i=1;i<=st[0];i++) cout<<st[i]<<" "; return 0; } else{ for(int i=1;i<=m;i++){ pig=0; memset(vis,0,sizeof(vis)); delu=se[i].u,delv=se[i].v; st[0]=1; st[1]=1; vis[1]=1; dfs(1,0); if(st[0]!=n) continue; bool ff=1; for(int j=1;j<=n;j++){ if(st[j]>da[j]){ff=0;break;} if(st[j]<da[j]){ff=1;break;} } if(ff){ for(int j=1;j<=n;j++) da[j]=st[j]; } } for(int j=1;j<=n;j++) cout<<da[j]<<" "; cout<<endl; return 0; } }
$update\space at\space 20190926$
突然想写一发 $O(n\log n)$ 的做法。
考虑如何对于基环树不删边求解,对于基环树可以对基环树的一个点暂时不走然后在通过另一侧的环经过。
然后记一下若能返悔最优的点即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<queue> #include<climits> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=500001; struct node{ int u,v,nex; }x[MAXN<<1]; int head[MAXN],vis[MAXN],cnt,n,m,INF=INT_MAX; void add(int u,int v){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } vector<int> vec; namespace Tree{ void dfs(int u,int fath){ priority_queue<int> que;vec.push_back(u); for(int i=head[u];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; que.push(-x[i].v); }while(!que.empty()){ int xx=-que.top();que.pop(); dfs(xx,u); } return; } void Solve(){ dfs(1,0); for(int i=0;i<vec.size();i++) printf("%d ",vec[i]);printf("\n");exit(0); } } namespace RingTree{ int cir[MAXN],vis[MAXN]; bool flag; void dfs1(int u,int fath){ vis[u]=1; for(int i=head[u];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; if(vis[x[i].v]) {cir[x[i].v]=cir[u]=1;flag=1;return;} dfs1(x[i].v,u); if(cir[x[i].v]&&flag){if(cir[u]) flag=0;cir[u]=1;return;} }return; } void dfs2(int u,int fath,int res){ if(vis[u]) return; vis[u]=1;vec.push_back(u); priority_queue<int> que; for(int i=head[u];i!=-1;i=x[i].nex){ if(x[i].v==fath||vis[x[i].v]) continue; que.push(-x[i].v); } while(!que.empty()){ int xx=-que.top();que.pop(); if(que.empty()&&cir[xx]&&flag&&xx>res){flag=0;return;} if(!que.empty()&&cir[u]) dfs2(xx,u,-que.top()); else dfs2(xx,u,res); }return; } void Solve(){ dfs1(1,0);memset(vis,0,sizeof(vis));flag=1;dfs2(1,0,INF); for(int i=0;i<vec.size();i++) printf("%d ",vec[i]);printf("\n");exit(0); } } int main(){ freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); memset(head,-1,sizeof(head)); n=read(),m=read(); for(int i=1;i<=m;i++){int u=read(),v=read();add(u,v),add(v,u);} if(m==n-1) Tree::Solve(); if(m==n) RingTree::Solve(); }
$5.game$
$65$分找规律。
考场代码(附$dfs$)
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int n,m,a[9][9],k,ans,cnt_dig[10001][81],cnt_zimu[100001][81],dig[101],zimu[101]; void dfs1(int x,int y){ if(x==n&&y==m){ cnt_dig[0][0]++; for(int i=1;i<=k;i++) cnt_dig[cnt_dig[0][0]][i]=dig[i],cnt_zimu[cnt_dig[0][0]][i]=zimu[i]; return; } if(x+1<=n){ zimu[++k]=1; dig[k]=a[x+1][y]; dfs1(x+1,y); k--; } if(y+1<=m){ zimu[++k]=2; dig[k]=a[x][y+1]; dfs1(x,y+1); k--; } return; } bool check(){ memset(dig,0,sizeof(dig)); memset(zimu,0,sizeof(zimu)); memset(cnt_dig,0,sizeof(cnt_dig)); memset(cnt_zimu,0,sizeof(cnt_zimu)); dfs1(1,1); bool ff=1; for(int i=1;i<=cnt_dig[0][0];i++){ for(int j=1;j<=cnt_dig[0][0];j++){ if(i==j) continue; int st=3; for(int p=1;p<=n+m-2;p++){ if(cnt_zimu[i][p]<cnt_zimu[j][p]){st=0;break;} if(cnt_zimu[i][p]>cnt_zimu[j][p]){st=1;break;} } if(st==1){ int sry=3; for(int p=1;p<=n+m-2;p++){ if(cnt_dig[i][p]>cnt_dig[j][p]){sry=0;break;} if(cnt_dig[i][p]<cnt_dig[j][p]){sry=1;break;} } if(sry==0)return 0; } } } return 1; } int tot; void dfs(int h,int l){ if(h==n+1){ if(check()) { ans++; ans%=mod; } return; } if(l!=m){ a[h][l]=1; dfs(h,l+1); a[h][l]=0; dfs(h,l+1); } else{ a[h][l]=1; dfs(h+1,1); a[h][l]=0; dfs(h+1,1); } } int query(int x,int y){ if(x==1){ if(y==1) return 2; if(y==2) return 4; if(y==3) return 8; } if(x==2){ if(y==1) return 4; if(y==2) return 12; if(y==3) return 36; } if(x==3){ if(y==1) return 8; if(y==2) return 36; if(y==3) return 112; } } signed main(){ freopen("game.in","r",stdin); freopen("game.out","w",stdout); n=read(),m=read(); if(n<=3&&m<=3){cout<<query(n,m);return 0;} if(n==1){ int ans=1; for(int i=1;i<=m;i++) ans*=2,ans%=mod; cout<<ans; return 0; } if(n==2){ int ans=4; for(int i=1;i<=m-1;i++) ans*=3,ans%=mod; cout<<ans; return 0; } if(n==3){ int ans=112; for(int i=1;i<=m-3;i++) ans*=3,ans%=mod; cout<<ans; return 0; } return 0; }
$6.defense$
$44$分的暴力
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct node{ int u,v,nex; }x[200001]; int n,m,val[100001],cnt,head[100001]; void add(int u,int v){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } int tot,dp[100001][2],px,bx,py,by,inf; char str[10]; void dfs(int f,int fath){ int son=0; for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; son++; } if(son==0){ if(f==px||f==py){ if(f==px){ if(bx==1) dp[f][1]=val[f],dp[f][0]=inf; if(bx==0) dp[f][0]=0,dp[f][1]=inf; } if(f==py){ if(by==1) dp[f][1]=val[f],dp[f][0]=inf; if(by==0) dp[f][0]=val[0],dp[f][1]=inf; } } else{ dp[f][0]=0; dp[f][1]=val[f]; } return; } for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; dfs(x[i].v,f); if(f==px||f==py){ if(f==px){ if(bx==1){ dp[f][1]+=min(dp[x[i].v][0],dp[x[i].v][1]); if(min(dp[x[i].v][0],dp[x[i].v][1])==inf) dp[f][1]=inf; } if(bx==0){ dp[f][0]+=dp[x[i].v][1]; if(dp[x[i].v][1]==inf) dp[f][0]=inf; } } if(f==py){ if(by==0){ dp[f][0]+=dp[x[i].v][1]; if(dp[x[i].v][1]==inf) dp[f][0]=inf; } if(by==1){ dp[f][1]+=min(dp[x[i].v][0],dp[x[i].v][1]); if(min(dp[x[i].v][0],dp[x[i].v][1])==inf) dp[f][1]=inf; } } } else{ dp[f][0]+=dp[x[i].v][1]; dp[f][1]+=min(dp[x[i].v][0],dp[x[i].v][1]); if(min(dp[x[i].v][0],dp[x[i].v][1])==inf) dp[f][1]=inf; if(dp[x[i].v][1]==inf) dp[f][0]=inf; } } if(dp[f][1]!=inf) dp[f][1]+=val[f]; return; } signed main(){ // freopen("defense.in","r",stdin); // freopen("defense.out","w",stdout); inf=2<<30-1; memset(head,-1,sizeof(head)); n=read(),m=read(); scanf("%s",str+1); for(int i=1;i<=n;i++) val[i]=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v),add(v,u); } while(m--){ px=read(),bx=read(),py=read(),by=read(); memset(dp,0,sizeof(dp)); dp[px][bx^1]=inf,dp[py][by^1]=inf; dfs(1,0); int k=min(dp[1][1],dp[1][0]); if(k>=inf){cout<<-1<<endl;continue;} printf("%lld\n",k); } }
然后直接动态$dp$即可。
update:分数线出来了,提高$300$,普及$190$。