2025省选模拟4
2025省选模拟4
题目来源: 2023 省选联测30
HZTG5872. 枇杷树
-
部分分
-
:模拟建图,统计答案时考虑每条边的贡献。点击查看代码
const ll p=1000000007; struct Graph { ll siz[100010]; int ans=0; vector<pair<int,int> >e[100010]; void add(int u,int v,int w) { e[u].push_back(make_pair(v,w)); } void merge(Graph another) { for(int i=0;i<=another.siz[0];i++) { for(int j=0;j<another.e[i].size();j++) { e[siz[0]+i].push_back(make_pair(another.e[i][j].first+siz[0],another.e[i][j].second)); } } } void dfs1(int x,int fa) { siz[x]=1; for(int i=0;i<e[x].size();i++) { if(e[x][i].first!=fa) { dfs1(e[x][i].first,x); siz[x]+=siz[e[x][i].first]; } } } void dfs2(int x,int fa) { for(int i=0;i<e[x].size();i++) { if(e[x][i].first!=fa) { ans=(ans+(siz[e[x][i].first]%p)*(siz[0]-siz[e[x][i].first])%p*e[x][i].second%p)%p; dfs2(e[x][i].first,x); } } } }d[20]; int main() { #define Isaac #ifdef Isaac freopen("loquat.in","r",stdin); freopen("loquat.out","w",stdout); #endif int m,x,y,u,v,w,i; cin>>m; d[0].siz[0]=1; for(i=1;i<=m;i++) { cin>>x>>y>>u>>v>>w; d[i]=d[x]; d[i].ans=0; d[i].merge(d[y]); d[i].add(u,d[i].siz[0]+v,w); d[i].add(d[i].siz[0]+v,u,w); d[i].dfs1(0,-1); d[i].dfs2(0,-1); cout<<d[i].ans<<endl; } return 0; }
-
:精细实现建图。
-
-
正解
- 考虑答案之间的继承关系。
- 设
表示 中所有节点到 的距离之和,有 。 - 难点在于怎么求
。观察到 ,不妨直接递归暴力计算。类似地,有 。 同样可以通过拆成三部分相加递归求解。- 因有用的
状态规模为 ,记忆化即可。
点击查看代码
const ll p=1000000007; ll siz[310],l[310],r[310],u[310],v[310],w[310],ans[310]; unordered_map<ll,unordered_map<ll,ll> >f,dis[310]; ll get_dis(ll i,ll x,ll y) { if(x>y) swap(x,y); if(dis[i][x].find(y)!=dis[i][x].end()) return dis[i][x][y]; if(y<siz[l[i]]) dis[i][x][y]=get_dis(l[i],x,y); else if(x>=siz[l[i]]) dis[i][x][y]=get_dis(r[i],x-siz[l[i]],y-siz[l[i]]); else dis[i][x][y]=((get_dis(l[i],x,u[i])+w[i])%p+get_dis(r[i],y-siz[l[i]],v[i]))%p; return dis[i][x][y]; } ll get_f(ll i,ll x) { if(f[i].find(x)!=f[i].end()) return f[i][x]; if(x>=siz[l[i]]) { f[i][x]=(get_f(l[i],u[i])+get_f(r[i],x-siz[l[i]]))%p; f[i][x]=(f[i][x]+get_dis(i,u[i],x)*(siz[l[i]]%p)%p)%p; } else { f[i][x]=(get_f(r[i],v[i])+get_f(l[i],x))%p; f[i][x]=(f[i][x]+get_dis(i,v[i]+siz[l[i]],x)*(siz[r[i]]%p)%p)%p; } return f[i][x]; } int main() { #define Isaac #ifdef Isaac freopen("loquat.in","r",stdin); freopen("loquat.out","w",stdout); #endif ll m,i; cin>>m; siz[0]=1; f[0][0]=0; dis[0][0][0]=0; for(i=1;i<=m;i++) { cin>>l[i]>>r[i]>>u[i]>>v[i]>>w[i]; siz[i]=siz[l[i]]+siz[r[i]]; ans[i]=(ans[l[i]]+ans[r[i]])%p; ans[i]=(ans[i]+get_f(l[i],u[i])*(siz[r[i]]%p)%p)%p; ans[i]=(ans[i]+get_f(r[i],v[i])*(siz[l[i]]%p)%p)%p; ans[i]=(ans[i]+w[i]*(siz[l[i]]%p)%p*(siz[r[i]]%p)%p)%p; cout<<ans[i]<<endl; } return 0; }
HZTG5873. 上古遗迹
-
设
的覆盖区间为 ,等价于询问 。 -
部分分
:两遍单调栈处理出 后 处理每组询问。 :对于同一左端点的询问一起处理,需要只跑一遍单调栈。
点击查看代码(时间复杂度有点假,不建议参考)
ll a[200010],st[200010],ed[200010],ans[200010]; stack<ll>s; set<ll>t; set<ll>::iterator it; vector<pair<ll,ll> >q[200010]; struct SMT { struct SegmentTree { ll maxx; }tree[800010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(ll rt) { tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); } void clear() { memset(tree,0,sizeof(tree)); } void update(ll rt,ll l,ll r,ll pos,ll val) { if(l==r) { tree[rt].maxx=val; return; } ll mid=(l+r)/2; if(pos<=mid) update(lson(rt),l,mid,pos,val); else update(rson(rt),mid+1,r,pos,val); pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) return tree[rt].maxx; ll mid=(l+r)/2; if(y<=mid) return query(lson(rt),l,mid,x,y); if(x>mid) return query(rson(rt),mid+1,r,x,y); return max(query(lson(rt),l,mid,x,y),query(rson(rt),mid+1,r,x,y)); } }T; int main() { #define Isaac #ifdef Isaac freopen("relics.in","r",stdin); freopen("relics.out","w",stdout); #endif ll n,m,l,r,i,j,k; scanf("%lld%lld",&n,&m); for(i=1;i<=n;i++) scanf("%lld",&a[i]); for(i=1;i<=m;i++) { scanf("%lld%lld",&l,&r); q[l].push_back(make_pair(r,i)); } for(i=1;i<=n;i++) { sort(q[i].begin(),q[i].end()); if(q[i].size()!=0) { T.clear(); t.clear(); while(s.empty()==0) s.pop(); for(j=0,k=i;j<q[i].size();j++) { for(it=t.begin();it!=t.end();it++) { T.update(1,1,n,*it,a[*it]*(q[i][j].first-st[*it]+1)); } for(;k<=q[i][j].first;k++) { while(s.empty()==0&&a[s.top()]>=a[k]) { ed[s.top()]=k-(a[s.top()]>a[k]); t.erase(s.top()); T.update(1,1,n,s.top(),a[s.top()]*(ed[s.top()]-st[s.top()]+1)); s.pop(); } st[k]=(s.empty()==0)?s.top()+1:i; T.update(1,1,n,k,a[k]*(q[i][j].first-st[k]+1)); s.push(k); t.insert(k); } ans[q[i][j].second]=T.query(1,1,n,i,q[i][j].first); } } } for(i=1;i<=m;i++) { printf("%lld\n",ans[i]); } return 0; }
-
正解
- 即使将式子拆成
也难以维护四个部分的值同时对答案的影响。 - 直接不管上面的限制,分讨得到
四种情况,不妨直接确定最终取值后取 。 - 前两种情况将修改和询问都挂在右端点上,在左端点上进行操作,因符合上述条件的同时也满足
的限制,故可以直接正反扫描线,单点修改、区间查询线段树维护。 - 第三种情况其对答案的贡献为
。以 为斜率, 为截距,查询 处最大值,正着扫描线的过程中李超线段树维护。 - 第四种情况同理。
点击查看代码
#define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) struct line { ll k,b; }li[200010]; ll a[200010],l[200010],r[200010],ans[200010]; stack<ll>s; vector<pair<ll,ll> >q1[200010],q2[200010],c[200010]; ll f(ll id,ll x) { return li[id].k*x+li[id].b; } bool cmp(ll a,ll b,ll x) { if(f(a,x)-f(b,x)>0) return true; if(f(b,x)-f(a,x)>0) return false; return a<b; } struct LiChao_Tree { struct SegmentTree { ll id; }tree[800010]; void clear() { memset(tree,0,sizeof(tree)); } void add(ll rt,ll l,ll r,ll id) { ll mid=(l+r)/2; if(cmp(tree[rt].id,id,mid)==false) swap(tree[rt].id,id); if(l==r) return; if(cmp(tree[rt].id,id,l)==false) add(lson(rt),l,mid,id); if(cmp(tree[rt].id,id,r)==false) add(rson(rt),mid+1,r,id); } void update(ll rt,ll l,ll r,ll x,ll y,ll id) { if(x<=l&&r<=y) { add(rt,l,r,id); return; } ll mid=(l+r)/2; if(x<=mid) update(lson(rt),l,mid,x,y,id); if(y>mid) update(rson(rt),mid+1,r,x,y,id); } ll query(ll rt,ll l,ll r,ll pos) { if(l==r) return f(tree[rt].id,pos); ll mid=(l+r)/2; if(pos<=mid) return max(f(tree[rt].id,pos),query(lson(rt),l,mid,pos)); else return max(f(tree[rt].id,pos),query(rson(rt),mid+1,r,pos)); } }T; struct SMT { struct SegmentTree { ll maxx; }tree[800010]; void pushup(ll rt) { tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); } void clear() { memset(tree,0,sizeof(tree)); } void update(ll rt,ll l,ll r,ll pos,ll val) { if(l==r) { tree[rt].maxx=max(tree[rt].maxx,val); return; } ll mid=(l+r)/2; if(pos<=mid) update(lson(rt),l,mid,pos,val); else update(rson(rt),mid+1,r,pos,val); pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) return tree[rt].maxx; ll mid=(l+r)/2,ans=0; if(x<=mid) ans=max(ans,query(lson(rt),l,mid,x,y)); if(y>mid) ans=max(ans,query(rson(rt),mid+1,r,x,y)); return ans; } }S; void solve1(ll n,ll m) { for(ll i=1;i<=n;i++) c[i].clear(); for(ll i=1;i<=n;i++) c[r[i]].push_back(make_pair(l[i],(r[i]-l[i]+1)*a[i])); S.clear(); for(ll i=1;i<=n;i++) { for(ll j=0;j<c[i].size();j++) S.update(1,1,n,c[i][j].first,c[i][j].second); for(ll j=0;j<q1[i].size();j++) ans[q1[i][j].second]=max(ans[q1[i][j].second],S.query(1,1,n,q1[i][j].first,i)); } } void solve2(ll n,ll m) { for(ll i=1;i<=n;i++) c[i].clear(); for(ll i=1;i<=n;i++) c[r[i]].push_back(make_pair(l[i],a[i])); S.clear(); for(ll i=n;i>=1;i--) { for(ll j=0;j<c[i].size();j++) S.update(1,1,n,c[i][j].first,c[i][j].second); for(ll j=0;j<q1[i].size();j++) ans[q1[i][j].second]=max(ans[q1[i][j].second],S.query(1,1,n,1,q1[i][j].first)*(i-q1[i][j].first+1)); } } void solve3(ll n,ll m) { for(ll i=1;i<=n;i++) c[i].clear(); li[0].b=-0x3f3f3f3f3f3f3f3f; for(ll i=1;i<=n;i++) { li[i]=(line){-a[i],a[i]*(r[i]+1)}; c[r[i]].push_back(make_pair(l[i],i)); } T.clear(); for(ll i=1;i<=n;i++) { for(ll j=0;j<c[i].size();j++) T.update(1,1,n,c[i][j].first,i,c[i][j].second); for(ll j=0;j<q1[i].size();j++) ans[q1[i][j].second]=max(ans[q1[i][j].second],T.query(1,1,n,q1[i][j].first)); } } void solve4(ll n,ll m) { for(ll i=1;i<=n;i++) c[i].clear(); li[0].b=-0x3f3f3f3f3f3f3f3f; for(ll i=1;i<=n;i++) { li[i]=(line){a[i],a[i]*(-l[i]+1)}; c[l[i]].push_back(make_pair(r[i],i)); } T.clear(); for(ll i=n;i>=1;i--) { for(ll j=0;j<c[i].size();j++) T.update(1,1,n,i,c[i][j].first,c[i][j].second); for(ll j=0;j<q2[i].size();j++) ans[q2[i][j].second]=max(ans[q2[i][j].second],T.query(1,1,n,q2[i][j].first)); } } int main() { #define Isaac #ifdef Isaac freopen("relics.in","r",stdin); freopen("relics.out","w",stdout); #endif ll n,m,ql,qr,i; scanf("%lld%lld",&n,&m); for(i=1;i<=n;i++) { scanf("%lld",&a[i]); while(s.empty()==0&&a[s.top()]>=a[i]) s.pop(); l[i]=(s.empty()==0)?s.top()+1:1; s.push(i); } while(s.empty()==0) s.pop(); for(i=n;i>=1;i--) { while(s.empty()==0&&a[s.top()]>=a[i]) s.pop(); r[i]=(s.empty()==0)?s.top()-1:n; s.push(i); } for(i=1;i<=m;i++) { scanf("%lld%lld",&ql,&qr); q1[qr].push_back(make_pair(ql,i)); q2[ql].push_back(make_pair(qr,i)); } solve1(n,m); solve2(n,m); solve3(n,m); solve4(n,m); for(i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
- 即使将式子拆成
HZTG5874. 吞天得手
-
部分分
:爆搜。
点击查看代码
const ll p=998244353; int a[100010],k; vector<int>s; struct quality { vector<int>s; bool operator < (const quality &another) const { for(int i=0;i<min(s.size(),another.s.size());i++) { if(s[i]<another.s[i]) return true; if(s[i]>another.s[i]) return false; } return s.size()<another.s.size(); } }; multiset<quality>q; multiset<quality>::iterator it; void dfs(int pos,int n) { if(pos==n+1) { if(s.size()!=0) { q.insert((quality){s}); if(q.size()>k) q.erase(--q.end()); } return; } s.push_back(a[pos]); dfs(pos+1,n); s.pop_back(); dfs(pos+1,n); } int main() { #define Isaac #ifdef Isaac freopen("ttds.in","r",stdin); freopen("ttds.out","w",stdout); #endif int n,base,ans,mi,i; scanf("%d%d%d",&n,&k,&base); for(i=1;i<=n;i++) { scanf("%d",&a[i]); } dfs(1,n); for(it=q.begin();it!=q.end();it++) { ans=0; mi=1; for(int i=it->s.size()-1;i>=0;i--) { ans=(ans+1ll*mi*it->s[i]%p)%p; mi=1ll*mi*base%p; } printf("%d\n",ans); } return 0; }
-
正解
- 考虑尽可能继承上一个转移而来的字符串。转移实际上是在
上进行 。 - 考虑分层进行扩展,使用优先队列辅助转移。扩展时枚举后缀中第
小值,不断进行回溯直至找满 个或后缀已经用完了。 - 后缀中第
小值使用主席树维护,沿途更新哈希值。 - 具体实现时应将每次相同的数都找出来一起处理。每层维护当前层的最优前缀的结尾位置集合,且这些前缀是相同的。更新时即向下一层提供状态,也向本层提供候选转移状态。每当队列空了或两者的前缀不一样了就进入下一层递归。
点击查看代码
const ll p=998244353; ll a[100010],n,k,base; vector<ll>pos[100010]; struct PDS_SMT { ll root[100010],rt_sum=0; struct SegmentTree { ll ls,rs,sum; }tree[100010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) ll build_rt() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(ll pre,ll &rt,ll l,ll r,ll pos,ll val) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum+=val; if(l==r) return; ll mid=(l+r)/2; if(pos<=mid) update(lson(pre),lson(rt),l,mid,pos,val); else update(rson(pre),rson(rt),mid+1,r,pos,val); } pair<ll,ll> query(ll rt1,ll rt2,ll l,ll r,ll k) { if(l==r) return make_pair(l,k); ll mid=(l+r)/2; if(tree[lson(rt2)].sum-tree[lson(rt1)].sum>=k) { return query(lson(rt1),lson(rt2),l,mid,k); } else { return query(rson(rt1),rson(rt2),mid+1,r,k-(tree[lson(rt2)].sum-tree[lson(rt1)].sum)); } } }T; struct quality { ll pre,hsh,k,pos; bool operator < (const quality &another) const { return a[pos]>a[another.pos]; } }; void dfs(priority_queue<quality>&q) { while(q.empty()==0) { quality x=q.top(),y; priority_queue<quality> nxt; while(q.empty()==0&&q.top().hsh==x.hsh) { x=q.top(); q.pop(); cout<<x.hsh<<endl; k--; if(k==0) exit(0); if(x.pos+1<=n)//为下一层提供状态 { y.pre=x.pos; y.k=1; pair<ll,ll>tmp=T.query(T.root[y.pre],T.root[n],1,100000,y.k); y.hsh=(x.hsh*base%p+tmp.first)%p; y.pos=pos[tmp.first][lower_bound(pos[tmp.first].begin(),pos[tmp.first].end(),y.pre+1)-pos[tmp.first].begin()+tmp.second-1]; nxt.push(y); } if(x.pre+x.k+1<=n)//为本层提供候选状态,将 k+1 加入集合 { y.pre=x.pre; y.k=x.k+1; pair<ll,ll>tmp=T.query(T.root[y.pre],T.root[n],1,100000,y.k); y.hsh=(x.hsh-a[x.pos]+tmp.first+p)%p; y.pos=pos[tmp.first][lower_bound(pos[tmp.first].begin(),pos[tmp.first].end(),y.pre+1)-pos[tmp.first].begin()+tmp.second-1]; q.push(y); } } dfs(nxt); } } int main() { #define Isaac #ifdef Isaac freopen("ttds.in","r",stdin); freopen("ttds.out","w",stdout); #endif ll minn=0x7f7f7f7f,id=0,i; cin>>n>>k>>base; priority_queue<quality>q; for(i=1;i<=n;i++) { cin>>a[i]; if(a[i]<minn) { minn=a[i]; id=i; } pos[a[i]].push_back(i); T.update(T.root[i-1],T.root[i],1,100000,a[i],1); } q.push((quality){0,minn,1,id}); dfs(q); return 0; }
- 考虑尽可能继承上一个转移而来的字符串。转移实际上是在
总结
- 想了一整场的
建虚树后层层递归怎么转移和 怎么一遍单调栈建笛卡尔树。 一开始把题读成了求字典序前 小的哈希值总和。口胡了个 的做法没过小样例后发现读假题了。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18669261,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
2024-01-13 初中信息奥赛模拟测试
2024-01-13 AT_arc125_c [ARC125C] LIS to Original Sequence 题解