近期模拟赛汇总
来让我们看看这个人到底在比赛中能干出什么呢
2025.10.8 国庆模拟赛二
T1
因为每个点只会被覆盖一次,所以倍增跳有标记的父亲然后暴力向下扩展就行。
来让我们看看这个人写的什么:
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
constexpr int N = 1e6+10;
int n,k,fa[N];
vector<int> G[N];
struct Q{
int id,l,r;
}q[N];
int dep[N],ans=2e9,siz[N],hson[N],dfn[N],dfx,top[N],cnt[N],idfn[N];
void dfs(int u){
dep[u]=dep[fa[u]]+1;siz[u]=1;
for(int i=0;i<(int )G[u].size();i++){
int v=G[u][i];
if(v==fa[u]) continue;
dfs(v);
siz[u]+=siz[v];
if(siz[v]>siz[hson[u]]) hson[u]=v;
}
}
void dfs2(int u,int tp){
top[u]=tp;dfn[u]=++dfx;
cnt[tp]++;idfn[dfx]=u;
if(hson[u]) dfs2(hson[u],tp);
for(int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if(v==fa[u] || v==hson[u]) continue;
dfs2(v,v);
}
}
struct SegTree{
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
ll tr[N<<2],tag[N<<2];
void pushup(int u){tr[u]=tr[ls]+tr[rs];}
void upd(int u,ll k,int len){tr[u]=k*len;tag[u]=k;}
void pushdown(int u,int l,int r){
if(!tag[u]) return;
upd(ls,tag[u],mid-l+1);upd(rs,tag[u],r-mid);
tag[u]=0;
}
void modify(int u,int l,int r,int x,int y,ll k){
if(l>=x && r<=y){upd(u,k,r-l+1);return ;}
pushdown(u,l,r);
if(x<=mid) modify(ls,l,mid,x,y,k);
if(mid<y) modify(rs,mid+1,r,x,y,k);
pushup(u);
}
ll query(int u,int l,int r,int x,int y){
if(l>=x && r<=y) return tr[u];
pushdown(u,l,r);ll res=0;
if(x<=mid) res+=query(ls,l,mid,x,y);
if(mid<y) res+=query(rs,mid+1,r,x,y);
return res;
}
}T;
inline void modify(int x,int y,ll k){
while(top[x]!=top[y]){
if(dep[x]<dep[y]) swap(x,y);
T.modify(1,1,n,dfn[top[x]],dfn[x],k);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
T.modify(1,1,n,dfn[x],dfn[y],k);
}
inline ll query(int x,int y){
ll res=0;
while(top[x]!=top[y]){
if(dep[x]<dep[y]) swap(x,y);
res+=T.query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return res+T.query(1,1,n,dfn[x],dfn[y]);
}
inline int ask(int x, int k) {
int tmp = dep[x] - k;
while (dep[top[x]] > tmp) x = fa[top[x]];
tmp = dep[x] - tmp;
return idfn[dfn[x] - tmp];
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=2;i<=n;i++){
cin>>fa[i];
G[fa[i]].push_back(i);
G[i].push_back(fa[i]);
}
for(int i=1;i<=k;i++){
cin>>q[i].id>>q[i].l>>q[i].r;
}
dfs(1);
dfs2(1,1);
for(int i=1;i<=k;i++){
int l=q[i].l,r=q[i].r,idx=q[i].id;
int tag;
tag=query(1,idx);
if(r-l+1>=dep[idx]-1-tag){
ans=min(max(dep[idx]-1-tag-1,0)+l,ans);
break;
}else{
int id=ask(idx,dep[idx]-(r-l+1)-1-tag);
int s=ask(idx,dep[idx]-2-tag);
modify(s,id,1);
}
}
cout << (ans==2e9 ? -1 : ans) << '\n';
return 0;
}
什么你说你没看懂,没事我给你说。
观察到第一个到达的点一定是最优的,所以考虑依次判断所有点是否可达。
然后发现操作实际上是从某节点向目标节点走若干步对吧。
直接树上链修,然后再链求和,还需要写个树上 k 级祖先。
没了,没有观察到重要性质导致的。
2025.10.13 CSP/NOIP 模拟赛6
T1
这玩意不一眼势能线段树,忘了 pushup 调了一小时。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
constexpr int N = 1e5+10;
constexpr int inf = 2e9;
int T,n,m,a[N];
struct Tree{
int fl,fr,cnt;
ll mn;
}tr[N<<2];
Tree Merge(const Tree &a,const Tree &b){
Tree res={0,0,0,0};
res.cnt=a.cnt+b.cnt-(a.fr==1 && b.fl==1);
res.fl=a.fl;res.fr=b.fr;
res.mn=min(a.mn,b.mn);
return res;
}
struct SegTree{
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
int tag[N<<2];
inline void pushup(int u){tr[u]=Merge(tr[ls],tr[rs]);}
void build(int u,int l,int r){
tr[u].mn=2e9;tag[u]=0;
if(l==r){tr[u]={1,1,1,a[l]};return ;}
build(ls,l,mid);build(rs,mid+1,r);
pushup(u);
}
inline void pushdown(int u){
if(!tag[u]) return ;
tr[ls].mn-=tag[u];tr[rs].mn-=tag[u];
tag[ls]+=tag[u];tag[rs]+=tag[u];
tag[u]=0;
}
Tree query(int u,int l,int r,int x,int y,int k){
if(tr[u].mn>=1e9+7){return {0,0,0,inf};}
if(l>=x && r<=y && tr[u].mn>k){tr[u].mn-=k,tag[u]+=k;return tr[u];}
if(l==r && tr[u].mn<=k){tr[u]={0,0,0,inf};return tr[u];}
Tree res={0,0,0,inf};pushdown(u);
if(x<=mid) res=query(ls,l,mid,x,y,k);
if(mid<y) res=Merge(res,query(rs,mid+1,r,x,y,k));
pushup(u);
return res;
}
}Tr;
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
int lst=0;
Tr.build(1,1,n);
for(int i=1,t;i<=m;i++){
cin>>t;
int tt=t-lst;lst=t;
cout << Tr.query(1,1,n,1,n,tt).cnt << ' ';
}
cout << '\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
solve();
}
return 0;
}
2025.10.15 CSP/NOIP 模拟赛7
T1
这倍增直接把最后的形态整出来,然后直接求最长的被烧过一段的长度-1 就是答案,写个线段树随便维护一下,轻松。
woc怎么调都不过去啊?哦它只会挨着火源放,哦它还只会放在最长的连续0的位置,那不用线段树,把那个位置卡住不倍增它,再扫一遍就行了。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
constexpr int N = 2e5+10;
int T,n,t,a[N],arr[N];
string s;
struct FIRE{
int l,r;
}d[N];int pre[N],nxt[N];
void solve() {
cin>>n>>t>>s;int tt=t;
s=' '+s;
for(int i=1; i<=n; i++)a[i]=s[i]-'0';
for(int i=n+1; i<=2*n; i++) a[i]=a[i-n];
for(int i=1;i<=2*n;i++) d[i]={i,i};
int mxd=0,mx=0,sum=0,lst=0;
for(int i=1; i<=2*n; i++) {
if(a[i]==1) {
if(mx<sum) {
mx=sum;
mxd=lst;
}
lst=i;
sum=0;
} else {
sum++;
}
}
if(mx<sum) mx=sum,mxd=lst;
for(int j=19; j>=0; j--) {
if((1<<j)<=tt) {
tt-=(1<<j);
for(int i=1; i<=2*n; i++) {
if(a[i]==1) {
d[i].l=max(d[i].l-(1<<j),1);
if(i!=mxd && i!=mxd+n)d[i].r=min(d[i].r+(1<<j),2*n);
}
}
}
if(tt==0) break;
}
int tmp=0;
for(int i=1; i<=2*n; i++) {
if(a[i]==1) pre[i]=tmp,tmp=i;
}
tmp=2*n+1;
for(int i=2*n; i>=1; i--) {
if(a[i]==1) nxt[i]=tmp,tmp=i;
}
for(int i=1; i<=2*n; i++) {
if(a[i]==1) d[i].l=max(d[i].l,pre[i]+1),d[i].r=min(d[i].r,nxt[i]-1);
}
for(int i=1; i<=2*n; i++) {
if(a[i]==1) {
arr[d[i].l]++;
arr[i]--;
arr[i+1]++;
arr[d[i].r+1]--;
}
}
int ans=0;
for(int i=1; i<=2*n; i++) {
arr[i]=arr[i-1]+arr[i];
}
for(int i=1;i<=n;i++){
arr[i]|=arr[i+n];
if(arr[i]==0 && a[i]==0) ans++;
}
if(ans==0) cout << ans << '\n';
else cout << ans-1 << '\n';
memset(arr,0,sizeof(arr));
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--) {
solve();
}
return 0;
}
T2
怎么有人忘了最短路能拼起来?
怎么有人忘了最短路能拼起来?
怎么有人忘了最短路能拼起来?
怎么有人忘了最短路能拼起来?
怎么有人忘了最短路能拼起来?
怎么有人忘了最短路能拼起来?
T3
我常常追忆过去
我常常追忆过去
我常常追忆过去
我常常追忆过去
我常常追忆过去
我常常追忆过去

浙公网安备 33010602011771号