【MX-S7】梦熊 NOIP 2024 模拟赛 3 & SMOI Round 2(同步赛)
\(T1\) luogu P11323 【MX-S7-T1】「SMOI-R2」Happy Card \(20pts\)
-
发现可以把「炸弹」也看做「三带一」。
-
先使用「三带一」带走原用于出「单牌」的牌,若「三带一」还有剩余则尝试带走原用于出「对子」的牌,否则直接出「单牌」和「对子」。
点击查看代码
ll a[300010],r[4]; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,ans,cnt,i,j; scanf("%lld",&t); for(j=1;j<=t;j++) { ans=cnt=0; memset(r,0,sizeof(r)); scanf("%lld",&n); for(i=1;i<=n;i++) { scanf("%lld",&a[i]); r[a[i]%3]++; cnt+=a[i]/3; } if(cnt>=r[1]) { cnt-=r[1]; ans+=r[1]; if(cnt>=2*r[2]) { cnt-=2*r[2]; ans+=2*r[2]; ans+=cnt/4*3+((cnt%4==3)?3:(2*(cnt%4!=0))); } else { r[2]-=cnt/2; ans+=cnt; ans+=r[2]; } } else { r[1]-=cnt; ans+=cnt+r[1]+r[2]; } printf("%lld\n",ans); } return 0; }
\(T2\) luogu P11324 【MX-S7-T2】「SMOI-R2」Speaker \(40pts\)
-
部分分
- \(40pts\)
- 设 \(h_{i}\) 表示从 \(i\) 出发不经过 \(x \to y\) 路径上的其他点所能到达的点的最大贡献 \(c-2dis\) 。则等价于询问 \(c_{x}+c_{y}-dis_{x,y}+\max\limits_{z \in (x \to y)} \{ h_{z} \}\) 。
点击查看代码
// #define Isaac namespace IO{ #ifdef Isaac FILE*Fin(fopen("in.in","r")),*Fout(fopen("out.out","w")); #else FILE*Fin(stdin),*Fout(stdout); #endif class qistream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qistream(FILE*_fp=stdin):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;}qistream&operator>>(char&x){x=getch();while(isspace(x))x=getch();return*this;}template<class T>qistream&operator>>(T&x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=x*10+buf[p]-'0';x=flag?-x:x;return*this;}char getch(){p+BLOCK>=SIZE?flush():void();return buf[p++];}qistream&operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i=0;for(;ch>' ';++i,ch=getch())str[i]=ch;str[i]='\0';return*this;}}qcin(Fin); class qostream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qostream(FILE*_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream&operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=-x,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while(x);for(int i=0,j=len-1;i<j;++i,--j)std::swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream&operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}qostream&operator<<(const char*s){for(int i=0;s[i];++i)putch(s[i]);return*this;}}qcout(Fout); } #define cin IO::qcin #define cout IO::qcout struct node { int nxt,to,w; }e[400010]; int head[400010],fa[400010],siz[400010],dep[400010],son[400010],top[400010],cnt=0; ll dis[400010],c[400010],maxx; bool vis[400010]; queue<int>q; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dis[e[i].to]=dis[x]+e[i].w; dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return dep[u]<dep[v]?u:v; } void dfs(int x,int fa,ll dis) { maxx=max(maxx,-2*dis+c[x]); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dfs(e[i].to,x,dis+e[i].w); } } } ll query(int u,int v,int n) { int rt=lca(u,v); ll ans=c[u]+c[v]-(dis[u]+dis[v]-2*dis[rt]); for(int i=1;i<=n;i++) { vis[i]=0; } maxx=0; vis[rt]=1; q.push(rt); for(;u!=rt;u=fa[u]) { vis[u]=1; q.push(u); } for(;v!=rt;v=fa[v]) { vis[v]=1; q.push(v); } while(q.empty()==0) { dfs(q.front(),0,0); q.pop(); } return ans+maxx; } int main() { int n,m,u,v,w,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs1(1,0); dfs2(1,1); for(i=1;i<=m;i++) { cin>>u>>v; cout<<query(u,v,n)<<endl; } return 0; }
- \(40pts\)
-
正解
- 发现“不经过 \(x \to y\) 路径上的其他点”这个限制没什么用,去掉后并不会对答案产生影响,推导过程同 luogu P2491 [SDOI2011] 消防 | luogu P1099 [NOIP2007 提高组] 树网的核 。
- 现在需要快捷地求出 \(h_{i}\) 表示从 \(i\) 出发所能到达的点的最大贡献 \(c-2dis\) ,赛时以为会和 luogu P6419 [COCI2014-2015#1] Kamp 一样换根分讨最长链、次长链的多种更新情况遂没写,但实际上因为当固定一些东西后,只有距离这一个变量(其实是距离的差值)所以还算比较好写。
- 设 \(f_{x}\) 表示从 \(x\) 出发到达以 \(x\) 为根的子树内的节点的最大贡献,状态转移方程为 \(f_{x}=\max(c_{x},\max\limits_{y \in Son(x)}\{ f_{y}-2w_{x \to y} \})\) ,边界为 \(g_{1}=f_{1}\) 。
- 然后进行换根,设 \(g_{x}\) 表示从 \(x\) 出发所能到达的点的最大贡献。状态转移方程为 \(g_{y}=\max(f_{y},g_{x}-2w_{x \to y}),y \in Son(x)\) 。
- 树剖加 \(ST\) 表或倍增暴力跳即可。
点击查看代码
struct node { ll nxt,to,w; }e[400010]; ll head[200010],c[200010],dis[200010],dep[200010],siz[200010],fa[200010],top[200010],son[200010],pos[200010],dfn[200010],f[200010],g[200010],cnt=0,tot=0; void add(ll u,ll v,ll w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } void dfs1(ll x,ll father) { f[x]=c[x]; siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dis[e[i].to]=dis[x]+e[i].w; dfs1(e[i].to,x); f[x]=max(f[x],f[e[i].to]-2*e[i].w); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void reroot(ll x) { for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]) { g[e[i].to]=max(f[e[i].to],g[x]-2*e[i].w); reroot(e[i].to); } } } void dfs2(ll x,ll id) { top[x]=id; tot++; dfn[x]=tot; pos[tot]=x; if(son[x]!=0) { dfs2(son[x],id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } struct ST { ll fmaxx[200010][25]; void init(ll n) { memset(fmaxx,-0x3f,sizeof(fmaxx)); for(ll i=1;i<=n;i++) { fmaxx[i][0]=g[pos[i]]; } for(ll j=1;j<=log2(n);j++) { for(ll i=1;i+(1<<j)-1<=n;i++) { fmaxx[i][j]=max(fmaxx[i][j-1],fmaxx[i+(1<<(j-1))][j-1]); } } } ll query(ll l,ll r) { ll t=log2(r-l+1); return max(fmaxx[l][t],fmaxx[r-(1<<t)+1][t]); } }T; ll query(ll u,ll v) { ll ans=c[u]+c[v]-dis[u]-dis[v],maxx=-0x3f3f3f3f; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { maxx=max(maxx,T.query(dfn[top[u]],dfn[u])); u=fa[top[u]]; } else { maxx=max(maxx,T.query(dfn[top[v]],dfn[v])); v=fa[top[v]]; } } if(dep[u]<dep[v]) { maxx=max(maxx,T.query(dfn[u],dfn[v])); return ans+2*dis[u]+maxx; } else { maxx=max(maxx,T.query(dfn[v],dfn[u])); return ans+2*dis[v]+maxx; } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,u,v,w,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs1(1,0); g[1]=f[1]; reroot(1); dfs2(1,1); T.init(n); for(i=1;i<=m;i++) { cin>>u>>v; cout<<query(u,v)<<endl; } return 0; }
\(T3\) luogu P11325 【MX-S7-T3】「SMOI-R2」Monotonic Queue \(0pts\)
- 正在理解题意。
\(T4\) luogu P11326 【MX-S7-T4】「SMOI-R2」XA-Game \(0pts\)
- 建议去看 luogu 题解 ,不补了。
总结
- 整场都在瞪 \(T1\) ,\(T3\) 基本没有进行转化题意。
- 写 \(T2\) 的过程中死了两次机,以为 \(O(nq)\) 可以直接冲 \(60pts\) ,然后发现自己写的常数太大了。