【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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifll 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 IsaacFILE*Fin(fopen("in.in","r")),*Fout(fopen("out.out","w"));#elseFILE*Fin(stdin),*Fout(stdout);#endifclass 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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifll 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\) ,然后发现自己写的常数太大了。