来让我们看看这个人到底在比赛中能干出什么呢
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
我常常追忆过去
我常常追忆过去
我常常追忆过去
我常常追忆过去
我常常追忆过去
我常常追忆过去