2026 NOI 做题记录(六)

news/2025/10/13 9:12:09/文章来源:https://www.cnblogs.com/DaiRuiChen007/p/19137699


Contest Link

\(\text{By DaiRuiChen007}\)



A. [P13540] 羊驼的坎坷之旅 (6)

Problem Link

首先图中点数太多,只考虑所有的 \((0,y)\) 类点,把每条路径按经过 \((0,y)\) 分段。

具体来说,设 \(w_y\) 表示第 \(y\) 列上极长的可通行前缀,那么路径经过某个 \(x\le w_y\)\((x,y)\) 就按 \((0,y)\) 分段。

现在我们只要考虑 \((0,u),(0,v)\)\(u<v\))能否在不被分段的情况下到互相达。

可以证明我们的策略一定是 \(1\sim \min(w_u,w_v)\)\(t_x\) 最大的 \(x\),然后直接从第 \(x\) 行走过去,即当且仅当 \(t_x>\max h[u,v]\)\((0,u),(0,v)\) 可达。

证明:如果这个 \(x\) 走不过去的话,那么 \(1\sim \min(w_u,w_v)\) 范围内任何一行都无法跨过 \(\arg\max h[u,v]\)

所以我们必须从一个 \(>\min(w_u,w_v)\) 的行跨过该列,这就要求我们经过某个 \((\min(w_u,w_v)+1,y)\),容易证明一定有 \(h_y<\max(h_u,h_v)\),从而 \(w_y>\min(w_u,w_v)\),这样这条路径就被 \((0,y)\) 分段了。

因此判断 \((u,v)\) 是否有边只要计算 \(\max t[1,\min(w_u,w_v)]>\max h[u,v]\) 是否成立即可。

注意到如果 \(i<j<k,w_i\le w_j\le w_k\),那么 \((i,k)\) 有边时 \((i,j),(j,k)\) 必定同时有边,则 \((i,k)\) 边不用加入。

因此我们只要找到每个 \(i\) 左侧右侧第一个 \(\ge w_i\) 的点并判断是否可以连边即可,这样可以通过 \(l=0,r=m-1\) 的子任务。

注意到被检验的边本质上就是对 \(h\) 建小根笛卡尔树后 \(i\) 的左右父亲。

那么两个点 \(u,v\) 连通当且仅当它们能跳到公共祖先上,也就是保留笛卡尔树上 \([l,r]\) 的点时,\(u,v\) 能向上到达最浅的祖先相同。

现在我们只要计算 \(u\)\([l,r]\) 范围内可达的最浅的祖先。

观察笛卡尔树上连边的结构:取出树上极长的同向链 \(a_1,a_2,\dots,a_k\) 满足 \(a_i\)\(a_{i-1}\) 的右父亲且任意 \((a_i,a_{i+1})\) 有边。

此时所有 \(a_i\) 的左父亲相同,设为 \(p\),注意到如果某个 \(a_i\)\(p\) 有边,则 \(a_{i+1},a_{i+2},\dots,a_m\)\(p\) 也有边,这是因为 \(\max h[p,a_{i+1}]<\max t[1,w_{a_i}]\le \max t[1,w_{a_{i+1}}]\)

所以我们想从 \(a_1\) 上跳到 \(p\) 时,只要找到最小的 \(a_k\)\(p\) 有边,然后从 \(a_k\) 跳到 \(p\) 即可,更大的点只会让路径超出 \([l,r]\) 范围,而不会到达更浅的祖先。

所以我们上跳的过程就是:如果 \(x\) 只和 \(\le 1\) 个父亲有边,那么直接跳过去,否则跳到较浅的父亲上。

这个过程可以用倍增维护,记录路径上的最小最大值即可。

当我们停止跳跃的时候,说明这个点只能向树边方向的父亲跳跃,无法到达非树边对应的父亲,只要向左和向右分别倍增一次即可。

时间复杂度 \(\mathcal O((m+q)\log m)\)

代码:

#include<bits/stdc++.h>
#include "obstacles.h"
using namespace std;
const int MAXN=2e5+5;
int n,m,a[MAXN],b[MAXN],w[MAXN],h[MAXN],z[MAXN][20],st[MAXN];
int qry(int l,int r) {int k=__lg(r-l+1);return max(z[l][k],z[r-(1<<k)+1][k]);
}
bool chk(int x,int y) {return w[min(a[x],a[y])]>qry(x,y);
}
int lf[MAXN],rf[MAXN],f[MAXN][20],vl[MAXN][20],vr[MAXN][20],L[MAXN][20],R[MAXN][20];
bool lc[MAXN],rc[MAXN];
void initialize(vector<int>T,vector<int>H) {n=T.size(),m=H.size();for(int i=1;i<=n;++i) w[i]=max(w[i-1],T[i-1]);for(int i=1;i<n;++i) T[i]=min(T[i],T[i-1]);for(int i=1;i<=m;++i) {z[i][0]=h[i]=H[i-1],a[i]=lower_bound(T.begin(),T.end(),h[i],greater<>())-T.begin();}for(int k=1;k<20;++k) for(int i=1;i+(1<<k)-1<=m;++i) {z[i][k]=max(z[i][k-1],z[i+(1<<(k-1))][k-1]);}for(int i=0;i<=m+1;++i) R[i][0]=rf[i]=m+1;vector <array<int,2>> o;for(int i=1;i<=m;++i) if(a[i]) o.push_back({h[i],i});sort(o.begin(),o.end());for(int i=0;i<(int)o.size();++i) b[o[i][1]]=i+1;for(int i=1,tp=0;i<=m;++i) if(a[i]) {for(;tp&&b[st[tp]]>b[i];--tp) rf[st[tp]]=i;lf[i]=st[tp],st[++tp]=i;}for(int i=1;i<=m;++i) if(a[i]) {if(lf[i]>=1&&(lc[i]=chk(lf[i],i))) L[i][0]=lf[i];if(rf[i]<=m&&(rc[i]=chk(i,rf[i]))) R[i][0]=rf[i];}for(int i=o.size()-1;~i;--i) {int x=o[i][1],d=b[lf[x]]<b[rf[x]];if(!lc[x]&&!rc[x]) f[x][0]=0;else if(!rc[x]) f[x][0]=lf[x];else if(!lc[x]) f[x][0]=rf[x];else f[x][0]=(d?lf[x]:rf[x]);vl[x][0]=vr[x][0]=f[x][0];}for(int k=1;k<20;++k) for(int i=0,p;i<=m+1;++i) {L[i][k]=L[L[i][k-1]][k-1],R[i][k]=R[R[i][k-1]][k-1],p=f[i][k-1],f[i][k]=f[p][k-1];vl[i][k]=min(vl[i][k-1],vl[p][k-1]),vr[i][k]=max(vr[i][k-1],vr[p][k-1]);}
}
int qry(int l,int r,int x) {for(int k=19;~k;--k) if(l<=vl[x][k]&&vr[x][k]<=r) x=f[x][k];while(l<=L[x][0]||R[x][0]<=r) {for(int k=19;~k;--k) if(L[x][k]>=l) x=L[x][k];for(int k=19;~k;--k) if(R[x][k]<=r) x=R[x][k];}return x;
}
bool can_reach(int l,int r,int u,int v) {++l,++r,++u,++v;return qry(l,r,u)==qry(l,r,v);
}



B. [P13691] Highest (4)

Problem Link

首先我们的策略可以看成从 \([l,r]=[a,a]\) 出发每次花 \(1\) 的代价把 \(r\) 变成 \(\max_{l\le i\le r} i+v_i\) 或花 \(2\) 的代价把 \(r\) 变成 \(\max_{l\le i\le r} i+w_i\)

考虑倍增维护该过程,问题是拼合两个长度为 \(2^k\) 的路径时,无法考虑形如 \((2^{k}-1)+2+(2^k-1)\) 的情况(即中间路径恰好被 \(2^{k}\) 切开)。

那么同时维护长度为 \(2^k,2^k-1\) 的最优路径 \(f,g\) 即可,但是这个时候我们要快速查询 \(f_{k},g_k\) 的区间最大值。

观察最优解形态,发现 \(f_k,g_k\) 的区间最大值一定在区间内 \(i+v_i\)\(i+w_i\) 的最大值处取到,因此只要维护这两个值的最大值下标即可。

查询到时候也维护长度为 \(ans,ans-1\) 的最优路径。

时间复杂度 \(\mathcal O((n+q)\log n)\)

代码:

#include<bits/stdc++.h>
#include "highest.h"
using namespace std;
const int MAXN=5e5+5;
int n,a[MAXN],b[MAXN],wa[MAXN][20],wb[MAXN][20],f[MAXN][20],g[MAXN][20];
inline int bit(int x) { return 1<<x; }
int cma(int x,int y) { return a[x]>a[y]?x:y; }
int cmb(int x,int y) { return b[x]>b[y]?x:y; }
int qa(int l,int r) { int k=__lg(r-l+1); return cma(wa[l][k],wa[r-bit(k)+1][k]); }
int qb(int l,int r) { int k=__lg(r-l+1); return cmb(wb[l][k],wb[r-bit(k)+1][k]); }
int qf(int l,int r,int k) { return max(f[qa(l,r)][k],f[qb(l,r)][k]); }
int qg(int l,int r,int k) { return max({r,g[qa(l,r)][k],g[qb(l,r)][k]}); }
vector<int>solve(vector<int>&V,vector<int>&W,vector<pair<int,int>>&Q) {n=V.size();for(int i=1;i<=n;++i) a[i]=min(n,V[i-1]+i),b[i]=min(n,W[i-1]+i),wa[i][0]=wb[i][0]=i;for(int k=1;k<20;++k) for(int i=1;i+bit(k)-1<=n;++i) {wa[i][k]=cma(wa[i][k-1],wa[i+bit(k-1)][k-1]);wb[i][k]=cmb(wb[i][k-1],wb[i+bit(k-1)][k-1]);}for(int i=1;i<=n;++i) f[i][0]=g[i][1]=a[i],g[i][0]=i,f[i][1]=max(a[qa(i,a[i])],b[i]);for(int k=2;k<20;++k) for(int i=1;i<=n;++i) {f[i][k]=max(qf(i,f[i][k-1],k-1),qg(i,b[qb(i,g[i][k-1])],k-1));g[i][k]=max(qf(i,g[i][k-1],k-1),qg(i,f[i][k-1],k-1));}vector <int> Z;for(auto o:Q) {int x=o.first+1,y=o.second+1,u=a[x],v=x,s=1;if(y<=a[x]) { Z.push_back(y>x); continue; } for(int k=19;~k;--k) {int q=max(qf(x,u,k),qg(x,b[qb(x,v)],k));if(q<y) s+=1<<k,v=max(qf(x,v,k),qg(x,u,k)),u=q;}Z.push_back(s+1);}return Z;
}



C. [P9732] Trade (4)

Problem Link

首先第一问是经典的决策单调性,主席树维护权值,分治求最优端点。

第二问我们只要找到若干最优区间,然后扫描线计算每个点是否 $\ge $ 该区间的第 \(k\) 大。

一个乱搞是找到每个左端点最远最近的最优右端点,以及每个右端点最远最近的最优左端点,但此时无法通过:只要在对应的端点周围再枚举 \(B=15\) 个点,如果是最优端点就加入即可。

首先可以证明区间权值 \(w\) 有四边形不等式,即 \(a<b<c<d\)\(w(a,d)+w(b,c)\le w(a,c)+w(b,d)\)

那么如果 \([a,d],[b,c]\) 都是最优区间,则 \([a,c],[b,d]\) 也是最优区间,所以无需考虑 \([a,d],[b,c]\)

具体来说我们枚举右端点,双指针找到所有合法左端点 \(l\),后续只要考虑大于当前 \(\max l\) 的左端点即可。

时间复杂度 \(\mathcal O(n\log^2 n)\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2.5e5+5,V=1e9+1;
struct SegmentTree {int ct[MAXN*32],ls[MAXN*32],rs[MAXN*32],tot=0;ll su[MAXN*32];void ins(int x,int l,int r,int q,int &p) {su[p=++tot]=su[q]+x,ct[p]=ct[q]+1;if(l==r) return ;int mid=(l+r)>>1;if(x<=mid) ins(x,l,mid,ls[q],ls[p]),rs[p]=rs[q];else ins(x,mid+1,r,rs[q],rs[p]),ls[p]=ls[q];}ll qks(int k,int l,int r,int q,int p) {if(l==r) return 1ll*k*l;int mid=(l+r)>>1,t=ct[rs[p]]-ct[rs[q]];if(k<=t) return qks(k,mid+1,r,rs[q],rs[p]);else return qks(k-t,l,mid,ls[q],ls[p])+su[rs[p]]-su[rs[q]];}int qkv(int k,int l,int r,int q,int p) {if(l==r) return l;int mid=(l+r)>>1,t=ct[rs[p]]-ct[rs[q]];if(k<=t) return qkv(k,mid+1,r,rs[q],rs[p]);else return qkv(k-t,l,mid,ls[q],ls[p]);}
}	T;
int n,k,rt[MAXN],g[MAXN],a[MAXN];
ll f[MAXN],b[MAXN];
ll ask(int l,int r) {return T.qks(k,1,V,rt[l-1],rt[r])-b[r]+b[l-1];
}
void cdq(int l,int r,int L,int R) {if(l>r) return ;int mid=(l+r)>>1;for(int i=L;i<=mid-k+1&&i<=R;++i) {ll z=ask(i,mid);if(f[mid]<=z) f[mid]=z,g[mid]=i;}cdq(l,mid-1,L,g[mid]),cdq(mid+1,r,g[mid],R);
}
struct BIT {int tr[MAXN],s;void init() { fill(tr,tr+n+1,V); }void upd(int x,int v) { for(;x;x&=x-1) tr[x]=min(tr[x],v); }int qry(int x) { for(s=V;x<=n;x+=x&-x) s=min(s,tr[x]); return s; }
}	S;
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>k;for(int i=1;i<=n;++i) cin>>b[i],b[i]+=b[i-1];for(int i=1;i<=n;++i) cin>>a[i],T.ins(a[i],1,V,rt[i-1],rt[i]);memset(f,-0x3f,sizeof(f)),cdq(k,n,1,n),S.init();ll z=*max_element(f+k,f+n+1);string w(n,'0'); int j=1;for(int i=1;i<=n;++i) if(f[i]==z) {for(;j<g[i];++j) {if(ask(j,i)==z) S.upd(i,T.qkv(k,1,V,rt[j-1],rt[i]));if(S.qry(j)<=a[j]) w[j-1]='1';}S.upd(i,T.qkv(k,1,V,rt[j-1],rt[i]));}for(;j<=n;++j) if(S.qry(j)<=a[j]) w[j-1]='1';cout<<z<<"\n"<<w<<"\n";return 0;
}



D. [P11425] 路南柯(4.5)

Problem Link

首先对于一条链,只要从两个端点分别输出 dfs 序即可。

那么一个简单的构造就是从每个叶子输出 dfs 序,此时任意两个叶子之间的路径都以正序和逆序出现,故中间路径被确定。

然后考虑菊花:我们从根开始顺序和逆序分别搜索所有儿子即可。

那么尝试用类似的结构优化链的构造:我们把叶子删掉,对于剩下部分跑上面的构造,然后第奇数个排列顺序搜叶儿子,第偶数个排列逆序搜索。

可以发现这个做法就是正确的,证明考虑每个叶子如何区分父亲和祖父,容易发现必须要选一个其父亲子树内的点为根 dfs,因此删掉所有叶子后剩下的每个叶子都需要 dfs 一次。

记得特判菊花。

时间复杂度 \(\mathcal O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
vector <int> G[MAXN],E[MAXN];
bool f[MAXN],o;
void dfs(int u,int fz) {for(int v:G[u]) if(v^fz) dfs(v,u);if(o) for(int x:E[u]) cout<<x<<" ";else for(int i=E[u].size()-1;~i;--i) cout<<E[u][i]<<" ";cout<<u<<" ";
}
void solve() {int n,c=0; cin>>n;for(int i=1;i<=n;++i) G[i].clear(),E[i].clear();for(int i=1,u,v;i<n;++i) cin>>u>>v,G[u].push_back(v),G[v].push_back(u);for(int i=1;i<=n;++i) f[i]=G[i].size()==1;for(int i=1;i<=n;++i) {vector <int> R;for(int x:G[i]) (f[x]||f[i]?E[i]:R).push_back(x);G[i].swap(R);}for(int i=1;i<=n;++i) if(G[i].size()==1) ++c;if(!c) {int x=find(f+1,f+n+1,0)-f;cout<<"2\n";o=0,dfs(x,0),cout<<"\n";o=1,dfs(x,0),cout<<"\n";return ;}cout<<c<<"\n",o=0;for(int i=1;i<=n;++i) if(G[i].size()==1) o^=1,dfs(i,0),cout<<"\n";
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int _; cin>>_;while(_--) solve();return 0;
}



E. [P11164] Permutations (2)

Problem Link

对于一个确定的前缀 \(a\),如果 LDS 长度 \(\ge 3\) 则无解;如果长度 \(=2\),找到末尾的最大值 \(y\),那么不能剩下 \(<y\) 的元素。

然后考虑 \(\max(a)\):剩余的 \(<\max(a)\) 的元素必须升序排列,而 \(>\max(a)\) 的元素可以任意排列。

\(<\max(a)\) 的元素可以插入在 \(>\max(a)\) 元素的极长上升前缀中。

\(f_{i,k}\) 表示 \(1\sim i\) 排列极长上升前缀长度 \(= k\) 的方案数,转移就是 \(f_{i,k}=\sum_{j\ge k-1} f_{i-1,j}\)

相当于 \(k\) 可以转移到 \([1,k+1]\),看成走一步 \(+1\) 和若干步 \(-1\)

那么 \(f_{i,k}\) 就是有 \(i-1\)\(+1\)\(i-k\)\(-1\) 的格路,满足前缀和 \(>0\),可以用反射容斥表示成组合数。

考虑上 \(<\max(a)\) 的元素后可以写成上指标卷积的形式 \(\mathcal O(1)\) 计算。

时间复杂度 \(\mathcal O((n+q)\log n)\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=3e5+5,MOD=1e9+7;
ll ksm(ll a,ll b=MOD-2) { ll s=1; for(;b;a=a*a%MOD,b>>=1) if(b&1) s=s*a%MOD; return s; }
ll fac[MAXN*2],ifac[MAXN*2];
ll C(int x,int y) { return y<0||y>x?(x==-1&&y==-1):fac[x]*ifac[y]%MOD*ifac[x-y]%MOD; }
int n,q,a[MAXN],L[MAXN],R[MAXN],d[MAXN],ans[MAXN];
struct BIT {int tr[MAXN],s;void init() { memset(tr,0,sizeof(tr)); }void add(int x,int v) { for(;x<=n;x+=x&-x) tr[x]=max(tr[x],v); }int qry(int x) { for(s=0;x;x&=x-1) s=max(s,tr[x]); return s; }
}	T,V;
int wl[MAXN],wr[MAXN];
vector <int> O[MAXN];
vector <array<int,2>> Q[MAXN];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n;for(int i=fac[0]=ifac[0]=1;i<=2*n;++i) ifac[i]=ksm(fac[i]=fac[i-1]*i%MOD);memset(wl,0x3f,sizeof(wl)),memset(wr,0x3f,sizeof(wr));for(int i=1;i<=n;++i) {cin>>a[i],L[i]=T.qry(n-a[i]+1),T.add(n-a[i]+1,i);O[L[i]].push_back(i),wl[i]=min(wl[i-1],a[i]);}T.init(),memset(d,0x3f,sizeof(d));for(int i=n;i>=1;--i) {R[i]=n-T.qry(a[i])+1,T.add(a[i],n-i+1),d[L[i]]=min(d[L[i]],R[i]);d[i]=min(d[i],d[i+1]),wr[i]=min(wr[i+1],a[i]);}cin>>q;for(int i=1,l,r;i<=q;++i) {cin>>l>>r;if(r<d[l]) Q[l].push_back({r,i});}T.init();for(int i=n;i>=1;--i) {V.add(i,a[i]);for(int x:O[i]) T.add(x,a[x]);for(auto o:Q[i]) {int p=o[0];if(min(wl[i-1],wr[p+1])<T.qry(p)) continue;int x=n-V.qry(p),y=n-(p-i+1)-x;ans[o[1]]=(C(2*x+y-1,x+y-1)+MOD-C(2*x+y-1,x+y+1))%MOD;}}for(int i=1;i<=q;++i) cout<<ans[i]<<"\n";return 0;
}



F. [P10140] Island Vacation (2.5)

Problem Link

建立圆方树,大小为 \(2\) 的方点称为普通儿子,剩余称为环儿子。

考虑游走过程:首先可以经过 \(u\) 的若干环儿子然后走回来,然后选择一个儿子走下去或走到 \(u\) 的兄弟。

那么先从下到上处理出 \(g_u\) 表示 \(u\) 经过若干环儿子后走到兄弟或者普通儿子的概率;\(h_v\) 表示走到环儿子 \(v\) 的概率;\(e_u\) 表示在 \(u\) 结束的概率。

计算 \(g_u\) 时要维护所有环儿子的 \(P=\prod 1+g_vx\),暴力卷积;算 \(h_v\) 的时候相当于把 \(P\) 除掉一个单项式,简单回撤;\(e_u\) 可以直接减掉走到其他点的概率和。

然后从上到下 dfs 计算到每个点的概率 \(f_u\),转移平凡。

时间复杂度 \(\mathcal O(n^2)\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=20005,MOD=1e9+7;
vector <int> E[MAXN],G[MAXN];
int n,m,vc,d[MAXN],dfn[MAXN],low[MAXN],dcnt,st[MAXN],tp;
bool ins[MAXN];
void tarjan(int u) {dfn[u]=low[u]=++dcnt,st[++tp]=u,ins[u]=true;for(int v:E[u]) {if(!dfn[v]) {tarjan(v),low[u]=min(low[u],low[v]);if(low[v]>=dfn[u]) {for(G[u].push_back(++vc);ins[v];ins[st[tp--]]=false) G[vc].push_back(st[tp]);}} else low[u]=min(low[u],dfn[v]);}
}
ll inv[MAXN],p[MAXN],f[MAXN],g[MAXN],h[MAXN],w[MAXN],e[MAXN];
void dfs1(int u,int fz) {int k=0; w[0]=1;for(int x:G[u]) for(int v:G[x]) dfs1(v,x);for(int x:G[u]) if(G[x].size()>1) {g[x]=1,w[++k]=0;for(int v:G[x]) g[x]=g[x]*g[v]%MOD;for(int i=k;i;--i) w[i]=(w[i]+w[i-1]*g[x])%MOD;}int dg=d[u]-(u>1);for(int i=0,c=inv[dg]*p[u]%MOD;i<=k;++i) {g[u]=(g[u]+c*w[i])%MOD;if(i<k) c=c*2*inv[dg-2*i-2]%MOD*(i+1)%MOD*p[u]%MOD;}for(int x:G[u]) if(G[x].size()>1) {for(int i=0,c=inv[dg]*p[u]%MOD,z=1;i<k;++i) {h[x]=(h[x]+1ll*c*z)%MOD;c=c*2*inv[dg-2*i-2]%MOD*(i+1)%MOD*p[u]%MOD;z=(w[i+1]+(MOD-g[x])*z)%MOD;}} else h[x]=g[u];e[u]=G[fz].size()>1?(1+MOD-g[u])%MOD:1;for(int x:G[u]) {if(G[x].size()>1) e[u]=(e[u]+h[x]*2*(g[x]+MOD-1))%MOD;else e[u]=(e[u]+MOD-h[x])%MOD;}
}
void dfs2(int u) {for(int x:G[u]) {if(G[x].size()==1) {f[G[x][0]]=f[u]*h[x]%MOD;dfs2(G[x][0]); continue;}for(int i=0,z=f[u]*h[x]%MOD;i<(int)G[x].size();++i) {f[G[x][i]]=(f[G[x][i]]+z)%MOD,z=z*g[G[x][i]]%MOD;}for(int i=G[x].size()-1,z=f[u]*h[x]%MOD;~i;--i) {f[G[x][i]]=(f[G[x][i]]+z)%MOD,z=z*g[G[x][i]]%MOD;}for(int v:G[x]) dfs2(v);}
}
void solve() {cin>>n>>m,vc=n,dcnt=tp=0;for(int i=1;i<=n;++i) cin>>p[i],p[i]=(1+MOD-p[i])%MOD;for(int i=1,u,v;i<=m;++i) cin>>u>>v,E[u].push_back(v),E[v].push_back(u),++d[u],++d[v];tarjan(1),dfs1(1,0),f[1]=1,dfs2(1);for(int i=1;i<=n;++i) cout<<e[i]*f[i]%MOD<<" \n"[i==n];for(int i=1;i<=n;++i) E[i].clear(),d[i]=0,dfn[i]=low[i]=ins[i]=0;for(int i=1;i<=vc;++i) e[i]=w[i]=f[i]=g[i]=h[i]=0,G[i].clear();
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);inv[1]=1;for(int i=2;i<MAXN;++i) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;int _; cin>>_;while(_--) solve();return 0;
}



G. [P9907] Mostovi (2.5)

Problem Link

建立 dfs 树,树边可以简单处理,重点是返祖边 \(u\to v\)

那么首先 \(v\) 的其他儿子必须和 \(v\) 子树外有边,然后 \(u\) 的儿子必须和 \(u\) 子树外有边。

然后要么 \(T_v-T_u\)\(v\) 子树外有边,要么有一个 \(u\) 的儿子同时与 \(T_u-T_v\)\(v\) 子树外有边。

第一个条件可以看成 dfn 序上两个区间内的点,线段树维护最小祖先。

第二个条件只要对 \(u\) 的每个儿子求出向 \(u\) 子树外的所有边中最深和最浅的一条,能满足的 \(v\) 深度在一个区间内,可以线段树判定。

只要求 \(u\) 儿子向外边中最浅的一条,在 dfs 的过程中维护,处理完 \(u\) 的儿子后删掉到 \(u\) 有返祖边的儿子,线段树维护。

注意特殊处理根的出边。

时间复杂度 \(\mathcal O(m\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5,inf=1e9,N=1<<17;
vector <int> G[MAXN],E[MAXN],H[MAXN];
int n,m,d[MAXN],up[MAXN],lo[MAXN],dfn[MAXN],efn[MAXN],dcnt,sn[MAXN];
struct Segt {int tr[N<<1];Segt() { fill(tr,tr+(N<<1),-inf); }void add(int x,int v) { for(x+=N;x;x>>=1) tr[x]=max(tr[x],v); }void upd(int x,int v) { for(tr[x+=N]=v;x>1;x>>=1) tr[x>>1]=max(tr[x],tr[x^1]); }int qry(int l,int r) {int s=-inf;for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) {if(~l&1) s=max(s,tr[l^1]);if(r&1) s=max(s,tr[r^1]);}return s;}
}	TL,TR,TQ;
void dfs1(int u,int fz) {vector <int> e; lo[u]=d[u]=d[fz]+1,dfn[u]=++dcnt;for(int v:G[u]) if(v^fz) {if(!d[v]) {dfs1(v,u),lo[u]=min(lo[u],lo[v]),e.push_back(v);if(lo[v]>=d[u]) sn[u]=sn[u]?-1:v;} else if(d[v]<d[u]) H[u].push_back(v),E[v].push_back(u),lo[u]=min(lo[u],d[v]);}G[u].swap(e),efn[u]=dcnt,sort(H[u].begin(),H[u].end(),[&](int x,int y){ return d[x]<d[y]; }); if(H[u].size()) TL.upd(dfn[u],-d[H[u][0]]),TR.upd(dfn[u],d[H[u].back()]);
}
int st[MAXN];
bool ban[MAXN];
void dfs2(int u) {st[d[u]]=u;for(int v:G[u]) dfs2(v);for(int v:E[u]) H[v].pop_back(),TR.upd(dfn[v],H[v].size()?d[H[v].back()]:-inf);if(u==1) {if(G[u].size()==1&&G[G[u][0]].size()==1) --m;if(G[u].size()==2&&(G[G[u][0]].empty()||G[G[u][1]].empty())) --m;return ;}for(int v:G[u]) {if(sn[u]&&sn[u]!=v) continue;for(int w:G[v]) if(lo[w]>=d[u]) goto n1;--m; n1:;}if(sn[u]) return ;for(int v:G[u]) {int h=TR.qry(dfn[v],efn[v]);TQ.add(lo[v],h);if(lo[v]==h) ban[h]=true;}for(int v:H[u]) {int x=st[d[v]+1];if((sn[v]&&sn[v]!=x)||ban[d[v]]) continue;if(v==1||min(-TL.qry(dfn[x],dfn[u]-1),-TL.qry(efn[u]+1,efn[x]))<d[v]||TQ.qry(1,d[v]-1)>d[v]) --m;}for(int v:G[u]) TQ.upd(lo[v],-inf),ban[lo[v]]=false;
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1,u,v;i<=m;++i) cin>>u>>v,G[u].push_back(v),G[v].push_back(u);dfs1(1,0),dfs2(1),cout<<m<<"\n";return 0;
}



*H. [P13695] theseus (7)

Problem Link

首先考虑每条边要传递什么 \(1\) bit 信息,自然的想法就是边的方向,那么边权为 \(1\) 时编号从小到大,否则编号从大到小,这样变成了有向图。

首先建立 bfs 树,我们需要用合适的方法使得路径上的同层边只有 \(\mathcal O(\log n)\) 条。

自然想到 dsu 状物,也就是同层边从 \(siz\) 小的连向 \(siz\) 大的,但是如果这条边的两个端点已经连通就会导致一个点有多个出边。

考虑在行走的时候通过合适的方法选择出边,从而去掉这种边的影响。

行走时我们的信息只有标号,所以不妨让每个点走向出边中标号最大的一个。

那么我们在加入所有边 \((x,y)\) 的时候,如果 \(x\) 有出度,那么我们要保证 \(y\) 小于所有出边中标号的最大值。

那么我们要选择合适的顺序,使得所有 \(x\) 邻居中标号最大的一条优先访问。

可以证明按 \((\max(d_x,d_y),\min(x,y),\max(x,y))\) 三元组降序排列时,满足上述性质。

具体构造就是如果 \(x,y\) 有出边,就把起点定为有出边的点,如果深度不同就把起点定为深的,否则定为所在连通块最小的一个。

时间复杂度 \(\mathcal O(m\log m)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=10005;
vector <int> G[MAXN];
int d[MAXN],f[MAXN];
vector<int> label(int n,vector<pair<int,int>>S,int t) {int m=S.size();for(auto o:S) G[o.first].push_back(o.second),G[o.second].push_back(o.first);queue <int> Q; Q.push(t),memset(d,0x3f,sizeof(d)),d[t]=0;while(Q.size()) {int u=Q.front(); Q.pop();for(int v:G[u]) if(d[v]>d[u]+1) d[v]=d[u]+1,Q.push(v);}vector <array<int,4>> E(m);vector <int> c(m,0);for(int i=0;i<m;++i) {int x=S[i].first,y=S[i].second;E.push_back({max(d[x],d[y]),min(x,y),max(x,y),i});}sort(E.begin(),E.end(),greater<>());for(int i=1;i<=n;++i) f[i]=1;for(auto e:E) {int x=e[1],y=e[2];if(!f[x]||!f[y]) { c[e[3]]=!f[x]; continue; }if(d[y]<d[x]||(d[x]==d[y]&&f[y]>f[x])) f[y]+=f[x],f[x]=0,c[e[3]]=1;else f[x]+=f[y],f[y]=0;}return c;
}
int travel(int n,int u,vector<pair<int,int>>e) {int mx=0;for(auto o:e) if((u>o.first)^o.second) mx=max(mx,o.first);return mx;
}



I. [P10055] Good Game (5.5)

Problem Link

我们的操作等价于每次选择一个长度 \(\ge 2\) 的连续段删掉。

那么我们只关注每个连续段的长度是否 \(\ge 2\),可以写成一个 01 串记录每段长是否 \(\ge 2\)

考虑删除一个字符 \(1\),如果左右都有连续段,就会合成一个长度 \(\ge 2\) 的连续段,也就是把子串 \(?1?\) 变成 \(1\)

很显然删除开头和结尾的 \(1\) 只会在最后进行,因此我们的目标就是每次删掉某个 \(1\) 的左右邻居,最终让串只剩 \(1\)

先考虑长度为 \(2k+1\) 的情况,正中间为 \(1\) 的情况是平凡的。

对于剩余的情况打表发现合法当且仅当,正中间元素左右侧的首个 \(1\) 到两侧边界距离之和 \(\ge k\)

构造就是操作若干次左边的 \(1\),使得右边的 \(1\) 到两侧距离相等。

充分性就考虑这两个 \(1\) 中间 \(0\) 的个数不能超过两边元素个数。

那么长度为偶数的情况最终一定会消除成 \(11\),那么这两个 \(1\) 一定对应原串长度为奇数的一个前缀和后缀,因此我们只要判定每个前缀和后缀即可。

判定容易做到 \(\mathcal O(1)\)

时间复杂度 \(\mathcal O(n\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n,m,a[MAXN],f[MAXN],g[MAXN];
struct BIT {int tr[MAXN],s;void add(int x,int v) { for(;x<=n;x+=x&-x) tr[x]+=v; }int qry(int x) { for(s=0;x;x&=x-1) s+=tr[x]; return s; }
}	T;
vector <int> w;
bool chk(int l,int r) {int x=(l+r)/2;if(a[x]) return true;return l<r&&max(0,f[x]-l)+max(0,r-g[x])>=(r-l)/2;
}
void gen(int l,int r) {int x=(l+r)/2;if(a[x]) {for(int i=(r-l)/2;i--;) w.push_back(x);return ;}int u=g[x]-l,v=r-g[x];for(;u>v;u-=2) w.push_back(f[x]);for(;u--;) w.push_back(g[x]);
}
int p[MAXN],d[MAXN],l[MAXN],r[MAXN];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n;for(int i=1,o=0;i<=n;++i) {char c; cin>>c,T.add(i,1);if(o!=c) d[++m]=1,a[m]=0,o=c;else ++d[m],a[m]=1;}g[m+1]=m+1;for(int i=1;i<=m;++i) f[i]=a[i]?i:f[i-1];for(int i=m;i>=1;--i) g[i]=a[i]?i:g[i+1];if(m&1) {if(!chk(1,m)) return cout<<"-1\n",0;gen(1,m);} else {for(int i=1;i<=m;i+=2) if(chk(1,i)&&chk(i+1,m)) {gen(1,i),gen(i+1,m); goto ok;}return cout<<"-1\n",0; ok:;}r[0]=1,l[m+1]=m;for(int i=1,x=1;i<=m;++i) p[i]=x,x+=d[i],l[i]=i-1,r[i]=i+1;vector <array<int,2>> z;for(int x:w) {int u=l[x],v=r[x],t=T.qry(p[x]);T.add(p[x],-d[x]);if(d[x]&1) z.push_back({t,3}),d[x]-=3;for(;d[x];d[x]-=2) z.push_back({t,2});d[x]=d[u]+d[v],p[x]=p[u];l[x]=l[u],r[x]=r[v],r[l[x]]=l[r[x]]=x;}for(int x=r[0];x<=m;x=r[x]) {if(d[x]&1) z.push_back({1,3}),d[x]-=3;for(;d[x];d[x]-=2) z.push_back({1,2});}cout<<z.size()<<"\n";for(auto i:z) cout<<i[0]<<" "<<i[1]<<"\n";return 0;
}



J. [P13690] Board Games(4.5)

Problem Link

首先答案可以贪心得到:把每个连通块对应的极长区间分段,然后删掉连接不同段的边,不断重复直到合法。

容易证明这个贪心过程等价于每次选出极长的合法前缀划分。

那么我们只要对每个 \(l\) 求最大的 \(r\) 使得 \([l,r]\) 合法。

考虑 \(l=1\) 的情况,我们肯定要每个点到 \(1\) 的路径最大值最小,因此求小根 Kruskal 重构树即可。

对每个 \(l\) 维护时就降序扫描线,LCT 动态维护最小生成树,线段树找最大的 \(r\) 满足 \(\le r\) 的边数 \(=r-l\) 即可。

时间复杂度 \(\mathcal O(n\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
struct Segt {int tg[1<<18];array<int,2> mx[1<<18];void psu(int p) { mx[p]=max(mx[p<<1],mx[p<<1|1]); }void adt(int p,int k) { mx[p][0]+=k,tg[p]+=k; }void psd(int p) { if(tg[p]) adt(p<<1,tg[p]),adt(p<<1|1,tg[p]),tg[p]=0; }void init(int l,int r,int p) {mx[p]={-2,r};if(l==r) return ;int mid=(l+r)>>1;init(l,mid,p<<1),init(mid+1,r,p<<1|1);}void add(int ul,int ur,int k,int l,int r,int p) {if(ul<=l&&r<=ur) return adt(p,k);int mid=(l+r)>>1; psd(p);if(ul<=mid) add(ul,ur,k,l,mid,p<<1);if(mid<ur) add(ul,ur,k,mid+1,r,p<<1|1);psu(p);}
}	T;
int ch[MAXN][2],fa[MAXN],mx[MAXN];
bool rv[MAXN];
void psu(int p) { mx[p]=max({mx[ch[p][0]],p,mx[ch[p][1]]}); }
void adt(int p) { if(p) rv[p]^=1,swap(ch[p][0],ch[p][1]); }
void psd(int p) { if(rv[p]) adt(ch[p][0]),adt(ch[p][1]),rv[p]=0; }
bool son(int p) { return p==ch[fa[p]][1]; }
bool isrt(int p) { return p!=ch[fa[p]][0]&&p!=ch[fa[p]][1]; }
void rot(int p) {int u=fa[p],v=fa[u],k=son(p);if(!isrt(u)) ch[v][son(u)]=p;ch[u][k]=ch[p][k^1],fa[ch[p][k^1]]=u;ch[p][k^1]=u,fa[u]=p,fa[p]=v;psu(u),psu(p);
}
void fls(int p) { if(!isrt(p)) fls(fa[p]); psd(p); }
void splay(int p) {for(fls(p);!isrt(p);rot(p)) if(!isrt(fa[p])) rot(son(p)==son(fa[p])?fa[p]:p);
}
int access(int p) {int u=0;for(;p;u=p,p=fa[p]) splay(p),ch[p][1]=u,psu(p);return u;
}
void mkrt(int p) { adt(access(p)); }
void link(int u,int v) { mkrt(u),splay(u),fa[u]=v; }
void cut(int u,int v) { mkrt(u),access(v),splay(v),ch[v][0]=fa[u]=0,psu(v); }
int fdrt(int u) {for(u=access(u),psd(u);ch[u][0];u=ch[u][0],psd(u));return u;
}
int s[MAXN],t[MAXN],f[MAXN];
vector <int> G[MAXN];
vector<int> partition_players(int n,int m,vector<int>X,vector<int>Y) {vector <array<int,2>> e;for(int i=0;i<m;++i) e.push_back({Y[i]+1,X[i]+1});sort(e.begin(),e.end());for(int i=1;i<=m;++i) s[i]=e[i-1][1],t[i]=e[i-1][0],G[s[i]].push_back(i);iota(mx+1,mx+n+m+1,1),T.init(1,n,1);for(int i=n;i>=1;--i) {T.add(i,i,2,1,n,1),T.add(i,n,-1,1,n,1);for(int x:G[i]) {int u=t[x];if(fdrt(i)!=fdrt(u)) {link(i,x+n),link(u,x+n);T.add(u,n,1,1,n,1);continue;}mkrt(i);int y=mx[access(u)]-n;if(x>=y) continue;if(u<t[y]) T.add(u,t[y]-1,1,1,n,1);cut(s[y],y+n),cut(t[y],y+n);link(i,x+n),link(u,x+n);}f[i]=T.mx[1][1];}vector <int> w;for(int x=1;x<=n;x=f[x]+1) w.push_back(f[x]-x+1);return w;
}



*K. [P13539] 恐龙大迁徙 (9.5)

Problem Link

先考虑直径过 \(0\) 的情况,朴素想法就是用最后 \(\log_4 n\) 位传递前 \(n-\log_4 n\) 位的最大深度,如果某个后 \(\log_4 n\) 位的数变成了新的直径端点,那么直接返回 \(4\) 即可。

然后考虑一般情况,那么我们用末尾 \(2\log n\) 位传递对应信息,加入一个点至多改变一个端点,但此时如果直径的某个端点被改变了,依然要传递另一个端点的信息,因此只能做到 \(Z=5\)

设直径为 \((x,y)\),上述做法的本质就是在维护 \(x,y\) 的可能集合 \(X,Y\),我们不一定要在最后的若干位记录每个点是否成为 \((x,y)\)

我们把序列末尾分成若干块,每块内缩减原先的 \(X,Y\),然后把块内元素插入 \(X,Y\) 中,可以得到一些更优的构造。

但这些构造每位的信息熵太低,我们考虑利用下标这个信息,具体来说只在一个块中选一个位置填非 \(0\) 元素,那么一个长度为 \(k\) 的块就能区分 \(4k+1\) 种情况。

不妨假设 \(|X|=|Y|\),那么一次操作大概能让 \(|X|\) 变成 \(\dfrac {|X|}{\sqrt{4k+1}}+k\)

注意第一个块之前我们可以钦定 \(x\le y\),那么样本空间大致会减半。

给出一组较优的块长序列:\([140,20,6,2,2]\),其中首次操作 \(4\times 140+1=\dfrac{33\times 34}2\),剩余操作 \(\sqrt {4k+1}\) 为整数,可以让 \(|X|\) 缩小 \([33,9,5,3,3]\)

实现的时候可以把 \(|X|,|Y|\) 按缩小的倍数均匀分块,然后传递 \(x,y\) 所属块标号即可。

这样操作到最后一定有 \(|X|,|Y|\le 5\),进一步优化:注意到每个块的首个元素可以在传递信息之前加入 \(|X|,|Y|\) 中并缩减,即 \(|X|\gets \dfrac{|X|+1}{\sqrt{4k+1}}+k-1\),这样最终能算出 \(|X|,|Y|\le 4\)

那么我们要用最后三次操作处理 \(|X|,|Y|\le 4\) 的情况。

  • 对于第一个点 \(n-3\),把 \(|X|,|Y|,n-3\) 看成九个点,可能的直径看成边,那么我们得到一张 \(9\) 个点 \(24\) 条边的图,由 \(K_{4,4}\) 和度数为 \(8\) 的点构成。
    考虑把该图分解成 \(5\) 个结构类似的子图,那么传递 \([0,4]\) 就能把样本空间缩减成一个子图,打表可以发现存在一组构造使得每个子图形如 \(K_{2,3}\) 删掉一条边。
  • 对于第二个点 \(n-2\),同上,我们得到一张 \(6\) 个点 \(10\) 条边的图,可以分解成 \(5\) 组长度为 \(2\) 的链。
  • 对于最后一个点 \(n-1\),我们得到一张只有 \(5\) 条边的图,直接传递即可。

模拟上述过程。

时间复杂度 \(\mathcal O(n\log n)\)

代码:

#include<bits/stdc++.h>
#include "migrations.h"
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
const int MAXN=1e4+5,B[7]={0,33,9,5,3,3,0},E[7]={0,9828,9968,9988,9994,9996,9998};
const int f[5][5]={{0,1,4,5,6},{1,0,7,8,6},{2,3,8,4,5},{6,7,2,3,8},{8,3,4,5,7}};
const int g[5][3]={{2,0,3},{0,4,5},{2,5,3},{2,1,3},{0,5,1}};
bool chk(int x,int y,int l,int r) { return min(x,y)==min(l,r)&&max(x,y)==max(l,r); }
namespace luotianyi {
int LCA(int x,int y) {if(dep[x]<dep[y]) swap(x,y);for(int k=19;~k;--k) if(dep[up[x][k]]>=dep[y]) x=up[x][k];if(x==y) return x;for(int k=19;~k;--k) if(up[x][k]^up[y][k]) x=up[x][k],y=up[y][k];return up[x][0];
}
int dis(int x,int y) { return dep[x]+dep[y]-dep[LCA(x,y)]*2; }
int x=1,y=1,o=0;
vector <int> S,T;
void upd(int z) {int l=dis(x,z),r=dis(z,y); o=max({o,l,r});if(o==l) y=z;else if(o==r) x=z;
}
int cx[MAXN],cy[MAXN],h;
int solve(int u,int fz) {dep[u]=dep[fz]+1,up[u][0]=fz;for(int k=1;k<20;++k) up[u][k]=up[up[u][k-1]][k-1];for(int b:{1,2,3,4,5}) {if(u==E[b+1]) {vector <int> ns,nt;for(int i:S) if(cx[i]==cx[x]) ns.push_back(i);for(int i:T) if(cy[i]==cy[y]) nt.push_back(i);S.swap(ns),T.swap(nt);}if(u==E[b]) {for(int i=E[b-1]+1;i<=E[b];++i) S.push_back(i),T.push_back(i),upd(i);for(int i=0,j=0,k=0,sx=S.size(),sy=T.size();i<B[b];++i) {for(int t=j+sx/B[b]+(i<sx%B[b]);j<t;++j) cx[S[j]]=i;for(int t=k+sy/B[b]+(i<sy%B[b]);k<t;++k) cy[T[k]]=i;}if(b>1) h=cx[x]*B[b]+cy[y];else {if(x<y) swap(x,y);h=cx[x]*(cx[x]+1)/2+cy[y];}}if(E[b]<=u&&u<E[b+1]&&h&&(h-1)/4==u-E[b]) return (h-1)%4+1;}if(u==n-2) {S.push_back(u-1),S.resize(4,u);T.push_back(u-1),T.resize(4,u);upd(u-1),upd(u),cx[8]=u;for(int i:{0,1,2,3}) cx[i]=S[i],cx[i+4]=T[i];for(int c:{0,1,2,3,4}) for(int i:{0,1}) for(int j:{2,3,4}) if(i<1||j<4)  {if(chk(x,y,cx[f[c][i]],cx[f[c][j]])) return h=c;}}if(u==n-1) {upd(u),cy[5]=u;for(int i:{0,1,2,3,4}) cy[i]=cx[f[h][i]];for(int c:{0,1,2,3,4}) for(int i:{0,1}) {if(chk(x,y,cy[g[c][i]],cy[g[c][i+1]])) return h=c;}}if(u==n) {upd(u),cx[3]=u;for(int i:{0,1,2}) cx[i]=cy[g[h][i]];for(int i=0,k=0;i<4;++i) for(int j=i+1;j<4;++j) if(i!=0||j!=2) {if(chk(x,y,cx[i],cx[j])) return k;++k;}}return 0;
}
}
namespace yuezhengling {
vector <int> S,T;
int n=10000,cx[MAXN],cy[MAXN];
pii solve(vector<int>a) {a.insert(a.begin(),0);for(int b:{1,2,3,4,5}) {for(int i=E[b-1]+1;i<=E[b];++i) S.push_back(i),T.push_back(i);for(int i=0,j=0,k=0,sx=S.size(),sy=T.size();i<B[b];++i) {for(int t=j+sx/B[b]+(i<sx%B[b]);j<t;++j) cx[S[j]]=i;for(int t=k+sy/B[b]+(i<sy%B[b]);k<t;++k) cy[T[k]]=i;}int h=0,hx=0,hy=0;for(int i=E[b];i<E[b+1];++i) if(a[i]) h=(i-E[b])*4+a[i];if(b>1) hx=h/B[b],hy=h%B[b];else for(int i=0;i<B[b];++i) for(int j=0;j<=i;++j) if(i*(i+1)/2+j==h) hx=i,hy=j;vector <int> ns,nt;for(int i:S) if(cx[i]==hx) ns.push_back(i);for(int i:T) if(cy[i]==hy) nt.push_back(i);S.swap(ns),T.swap(nt);}S.push_back(n-3),S.resize(4,n-2);T.push_back(n-3),T.resize(4,n-2);cx[8]=n-2;for(int i:{0,1,2,3}) cx[i]=S[i],cx[i+4]=T[i];cy[5]=n-1;for(int i:{0,1,2,3,4}) cy[i]=cx[f[a[n-2]][i]];cx[3]=n;for(int i:{0,1,2}) cx[i]=cy[g[a[n-1]][i]];for(int i=0,k=0;i<4;++i) for(int j=i+1;j<4;++j) if(i!=0||j!=2) {if((k++)==a[n]) return {cx[i]-1,cx[j]-1};}return {0,0};
}
}
int send_message(int n,int x,int fz) { return luotianyi::solve(x+1,fz+1); }
pii longest_path(vector<int>S) { return yuezhengling::solve(S); }



L. [P12033] Package Pickup (2)

Problem Link

考虑 dp,最优解一定是每个牛向一个方向走到头然后掉头。

\(f_{i,0/1/2/3}\) 表示考虑到第 \(i\) 个点,记录这个点被左边 / 右边的点经过,且是否在这一侧掉头。

容易把转移写成矩阵,记录区间左右端点就变成可合并信息。

\(m\) 分块,每块内所有信息乘积排序后线段树维护,若干连续且相同的块用快速幂加速计算。

时间复杂度 \(\mathcal O((n+p)\log m)\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=4e4+5,N=1<<16;
const ll inf=4e18;
struct Mat {ll f[4][4];Mat() { for(int i:{0,1,2,3}) for(int j:{0,1,2,3}) f[i][j]=inf; }inline friend Mat operator *(const Mat &u,const Mat &v) {Mat w;for(int i:{0,1,2,3}) for(int j:{0,1,2,3}) {w.f[i][j]=min({inf,u.f[i][0]+v.f[0][j],u.f[i][1]+v.f[1][j],u.f[i][2]+v.f[2][j],u.f[i][3]+v.f[3][j]});}return w;}
};
Mat bd(ll z) {Mat o;o.f[0][0]=o.f[3][3]=z,o.f[1][1]=o.f[2][2]=2*z;o.f[0][2]=o.f[0][3]=o.f[1][2]=o.f[1][3]=0;return o;
}
struct info {ll l,r; Mat f;info() { l=r=-1,f=Mat(); }inline friend info operator *(const info &u,const info &v) {if(u.l<0||v.l<0) return ~u.l?u:v;info w; w.l=u.l,w.r=v.r;w.f=u.f*bd(v.l-u.r)*v.f;return w;}inline friend info operator +(const info &u,const ll &d) {info w; w.l=u.l+d,w.r=u.r+d,w.f=u.f; return w;}
};
info f[N<<1],w[MAXN];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);ll m; int n1,n2;cin>>m>>n1>>n2;for(int i=1;i<=n1+n2;++i) {ll l,r; cin>>l>>r;w[i].l=l,w[i].r=r;if(i<=n1) w[i].f.f[2][0]=w[i].f.f[3][1]=0;else w[i].f.f[0][0]=w[i].f.f[1][1]=w[i].f.f[2][2]=w[i].f.f[3][3]=0;}sort(w+1,w+n1+n2+1,[&](auto x,auto y){ return x.l%m<y.l%m; });map <ll,vector<int>> sg;for(int i=1;i<=n1+n2;++i) {sg[w[i].l/m].push_back(i);sg[w[i].r/m+1].push_back(-i);w[i].r=w[i].l%=m;}info ans;for(auto it=sg.begin();it!=sg.end();++it) {auto nx=next(it);if(nx==sg.end()) break;ll l=it->first,r=nx->first;for(int x:it->second) {if(x>0) for(f[x+N]=w[x],x=(x+N)>>1;x;x>>=1) f[x]=f[x<<1]*f[x<<1|1];else for(f[x=N-x]=info(),x>>=1;x;x>>=1) f[x]=f[x<<1]*f[x<<1|1];}info z=f[1]; ll d=l*m;if(z.l<0) continue;for(int k=0;k<=__lg(r-l);++k) {if((r-l)>>k&1) 	ans=ans*(z+d),d+=m<<k;z=z*(z+(m<<k));}}ll z=inf;for(int x:{2,3}) for(int y:{0,1}) z=min(z,ans.f.f[x][y]);cout<<z<<"\n";return 0;
}



M. [P12485] PM 大师(5.5)

Problem Link

序列信息不好维护,在值域上考虑。

\(a_i=0\) 的下标为 \(b_1\sim b_m\),对于每个 \(v\in[1,n]\)\(c_v=\min\{i\mid a_i=v\}\)

那么对于 \(b_1\),找到最小的 \(v\) 满足 \(c_v>b_1\),然后把 \(c_v\) 设为 \(b_1\) 即可;对于 \(b_2\),在刚才的基础上找首个 \(c_v>b_2\),剩下的以此类推。

\(b_i\)\(c_{x_i}\) 设为了 \(b_i\),那么 \(x_i\) 本质上是最小的 \(v\) 满足 \(v>x_{i-1}\)\(c_v>b_i\)

然后维护单点修改,先考虑减小某个 \(c_v\gets y\) 的情况。

首先必定存在 \(x_i=v,y<b_i\),否则无影响,然后考虑新的 \(x_i\) 取值,首先新的 \(x_i\le x_{i+1}\),如果 \(x_i<x_{i+1}\),那么不会影响 \(x_{i+1}\sim m\),否则 \(x_i\gets x_{i+1}\),然后继续判断新的 \(x_{i+1}\) 能否取到 \(x_{i+2}\),以此类推。

那么考虑停止的时刻:就是首个 \(j\ge i\) 满足 \(\max c[x_{j}+1,x_{j+1}-1]>b_j\),注意到上述操作对 \(x_i\) 构成的集合 \(X\) 只有 \(\mathcal O(1)\) 影响。

\(w_v=\max \{i\mid b_i<c_v\}\),那么我们找到首个 \(z\not\in X\),满足 \([1,z]\)\(X\) 中元素个数等于 \(w_z\)

如果要增加 \(c_v\gets y\),找到最小的 \(x_i>v\),然后 \(x_i\gets v,x_{i+1}\gets x_i,\dots\),当某个 \(w_{x_j}=j\) 时停止,即首个 \(z\in X\) 满足 \([1,z)\)\(X\) 元素个数等于 \(w_z-1\)

线段树二分维护对应的 \(z\) 即可。

时间复杂度 \(\mathcal O((n+q)\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5,inf=1e9;
struct heap {priority_queue <int,vector<int>,greater<>> qi,qo;void ins(int x) { qi.push(x); }void ers(int x) { qo.push(x); }int top() {while(qo.size()&&qi.top()==qo.top()) qi.pop(),qo.pop();return qi.top();}
}	h[MAXN];
int n,m,q,a[MAXN],b[MAXN],c[MAXN],e[MAXN];
struct BIT {int tr[MAXN];void add(int x,int v) { for(;x<=n;x+=x&-x) tr[x]+=v; }int qs(int x) { int s=0; for(;x;x&=x-1) s+=tr[x]; return s; }int qk(int k) {int x=0;for(int i=1<<19;i;i>>=1) if(x+i<=n&&k>tr[x+i]) k-=tr[x+=i];return x+1;}
}	X;
struct Segt {int tr[1<<21|5][2],ad[1<<21|5];void psu(int p) { tr[p][0]=max(tr[p<<1][0],tr[p<<1|1][0]),tr[p][1]=min(tr[p<<1][1],tr[p<<1|1][1]); }void adt(int p,int k) { ad[p]+=k,tr[p][0]+=k,tr[p][1]+=k; }void psd(int p) { if(ad[p]) adt(p<<1,ad[p]),adt(p<<1|1,ad[p]),ad[p]=0; }void init(int l=1,int r=n,int p=1) {tr[p][0]=0,tr[p][1]=inf;if(l==r) return ;int mid=(l+r)>>1;init(l,mid,p<<1),init(mid+1,r,p<<1|1);}void chg(int x,int o,int l=1,int r=n,int p=1) {if(l==r) return tr[p][o]=tr[p][o^1],tr[p][o^1]=o?-inf:inf,void();int mid=(l+r)>>1; psd(p);x<=mid?chg(x,o,l,mid,p<<1):chg(x,o,mid+1,r,p<<1|1);psu(p);}void add(int ul,int ur,int k,int l=1,int r=n,int p=1) {if(ul>ur) return ;if(ul<=l&&r<=ur) return adt(p,k);int mid=(l+r)>>1; psd(p);if(ul<=mid) add(ul,ur,k,l,mid,p<<1);if(mid<ur) add(ul,ur,k,mid+1,r,p<<1|1);psu(p);}int find(int x,int o,int l=1,int r=n,int p=1) {if(tr[p][o]!=o) return 0;if(l==r) return l;int mid=(l+r)>>1; psd(p);int z=x<=mid?find(x,o,l,mid,p<<1):0;return z?z:find(x,o,mid+1,r,p<<1|1);}
}	T;
void ins(int x){int y=e[h[x].top()],i=X.qs(x-1)+1;T.add(x,x,y-c[x]),c[x]=y;if(X.qk(i)!=x||y>=i) return ;int p=x<n?T.find(x+1,0):0;if(p) T.add(p+1,n,-1),T.chg(p,1),X.add(p,1);T.add(x+1,n,1),T.chg(x,0),X.add(x,-1);
}
void ers(int x) {int y=e[h[x].top()],i=X.qs(x-1)+1;T.add(x,x,y-c[x]),c[x]=y;if(X.qk(i)==x||y<i) return ;int p=x<n?T.find(x+1,1):0;if(p) T.add(p+1,n,1),T.chg(p,0),X.add(p,-1);T.add(x+1,n,-1),T.chg(x,1),X.add(x,1);
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>q;for(int i=1;i<=n;++i) h[i].ins(n+1);for(int i=1;i<=n;++i) {cin>>a[i],e[i]=e[i-1]+(!a[i]);if(a[i]>0) h[i].ins(i);}e[n+1]=e[n],T.init();for(int i=1,z=0;i<=n;++i) {c[i]=e[h[i].top()],T.add(i,i,c[i]);if(c[i]>z) X.add(i,1),++z,T.chg(i,1),T.add(i+1,n,-1);}for(int x,k,y;q--;) {cin>>x>>k>>y;if(~a[x]) h[a[x]].ers(x),ers(a[x]);a[x]=k;if(~a[x]) h[a[x]].ins(x),ins(a[x]);cout<<(a[y]?a[y]:X.qk(e[y]))<<"\n";}return 0;
}



*N. [P9353] 现代机器 (7)

Problem Link

手玩一下发现每次操作都是给一个前缀染红或者一个后缀染蓝,设起点为 \(x\),具体的方向和范围只和 \([1,x)\) 中红色点数以及 \((x,n]\) 中蓝色点数有关。

考虑操作若干次后,序列会变成前缀 \(k\) 个红点以及后缀 \(n-k\) 个蓝点,对于每个起点 \(x\),手动分讨得到新的 \(k\)\((k+x+[x>k])\bmod (n+1)\),可以用线段树暴力维护分段函数,可以证明 \(k\) 个函数的复合只有 \(\mathcal O(k)\) 段,查询的时候只要在每个节点的函数上单点求值。

那么我们只要找到首个能形成上述结构的位置。

进一步刻画操作,发现就是把左边 \(x\) 个蓝染红或者把右边 \(n-x+1\) 个红染蓝。

感性理解操作不会很多,设前缀红点数为 \(l\),后缀蓝点数为 \(r\),那么 \(l<x\le n-r\)\(\min(l,r)\) 翻倍。

所以 \(l<x\le n-r\) 的操作只有 \(\mathcal O(\log n)\) 次,可以暴力模拟这些操作。

我们只要对当前局面快速找到首个 \(l<x\le n-r\) 的位置,但是对每对 \((l,r)\) 都处理每个点的答案太困难了。

考虑把 \((l,r)\) 压缩成 \((2^{\log l},2^{\log r})\),这样就只有 \(\mathcal O(\log^2n)\)\((l,r)\),可以对每个位置求出下一个 \(x\),显然此时每次操作也会让 \(\min(l,r)\) 的二进制最高位 \(+1\)

模拟上述过程即可,注意初始 \(x\) 为蓝色时操作有一些细节。

时间复杂度 \(\mathcal O((n+q)\log^2n )\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1.2e5+5;
int n,m,q,a[MAXN];
struct Segt {vector <array<int,2>> f[1<<18|5];auto find(int p,int k) {return lower_bound(f[p].begin(),f[p].end(),array<int,2>{k,-n});}void init(int l=1,int r=m,int p=1) {if(l==r) {if(a[l]==n) f[p]={{n-1,0},{n,-1}};else if(2*a[l]>n) f[p]={{n-a[l]-1,a[l]+1},{a[l]-1,a[l]-n},{n,a[l]-n-1}};else f[p]={{a[l]-1,a[l]+1},{n-a[l],a[l]},{n,a[l]-n-1}};return ;}int mid=(l+r)>>1,x=p<<1,y=p<<1|1,L=0;init(l,mid,x),init(mid+1,r,y);for(auto i:f[x]) {int R=i[0],z=i[1];auto s=find(y,L+z),t=find(y,R+z);for(;s<t;++s) f[p].push_back({(*s)[0]-z,z+(*s)[1]});f[p].push_back({R,z+(*s)[1]}),L=R+1;}}void ask(int ul,int ur,int &x,int l=1,int r=m,int p=1) {if(ul<=l&&r<=ur) return x+=(*find(p,x))[1],void();int mid=(l+r)>>1;if(ul<=mid) ask(ul,ur,x,l,mid,p<<1);if(mid<ur) ask(ul,ur,x,mid+1,r,p<<1|1);}
}	T;
int sb[MAXN],qr[MAXN],cr,ql[MAXN],cl,f[18][18][MAXN];
ll gl[18][MAXN],gr[18][MAXN];
char ch[MAXN];
int sol(int l,int r,int k) { T.ask(l,r,k); return k; }
int qry(int x,int y) {int l=1,r=1,lo,ro,z;auto qo=[&](int i) { return i?__lg(i)+1:0; };auto isb=[&](int i) { return i<=ql[l]?0:(i>n-qr[r]?1:ch[i]=='B'); };auto ctb=[&]() { return qr[r]+sb[n-qr[r]]-sb[ql[l]]; };auto nl=[&](int s,int t) { return min((ll)cl,l+min(gl[lo][t]-gl[lo][s-1],(ll)n)); };auto nr=[&](int s,int t) { return min((ll)cr,r+min(gr[ro][t]-gr[ro][s-1],(ll)n)); };for(;x<=y;++x) {lo=qo(ql[l]),ro=qo(qr[r]),z=min(y+1,f[lo][ro][x]);for(int k=1<<__lg(z-x+1);k;k>>=1) if(x+k<=z&&ql[nl(x,x+k-1)]+qr[nr(x,x+k-1)]<n) {l=nl(x,x+k-1),r=nr(x,x+k-1),x+=k;}if(x>y) break;int p=a[x]+isb(a[x]),c=ctb();if(c>=p) {if(p>=c-qr[r]) return sol(x+1,y,n-c+p);l+=p;} else {c=n-c,p=n+1-p;if(p>=c-ql[l]) return sol(x+1,y,c-p);r+=p;}}return n-ctb();
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=n;++i) {cin>>ch[i],sb[i]=sb[i-1]+(ch[i]=='B');if(ch[i]=='B') ql[++cl]=i-1;}for(int i=1;i<=m;++i) cin>>a[i];for(int i=n;i>=1;--i) if(ch[i]=='R') qr[++cr]=n-i;ql[++cl]=n,qr[++cr]=n;for(int x=0;x<18;++x) for(int y=0;y<18;++y) {int l=x?1<<(x-1):0,r=y?1<<(y-1):0;for(int i=m+1;i>=1;--i) f[x][y][i]=i>m||(l<a[i]&&a[i]<=n-r)?i:f[x][y][i+1];}for(int x=0;x<18;++x) {int d=x?1<<(x-1):0;for(int i=1;i<=m;++i) {gl[x][i]=gl[x][i-1]+(a[i]<=d?a[i]:0);gr[x][i]=gr[x][i-1]+(a[i]>n-d?n-a[i]:0);}}T.init(),cin>>q;for(int l,r;q--;) cin>>l>>r,cout<<qry(l,r)<<"\n";return 0;
}



O. [P10209] 路网服务 (5)

Problem Link

对原图缩点,我们只关心每个连通块占据了行的哪个区间,操作就是花 \(c_i\) 代价让所有过 \(i\) 的区间连通。

因此询问时如果有两个互相包含的区间,只要保留长度最小点一个。

先考虑 \(c_i=1\) 的情况,此时我们从最左边区间的右端点出发,每次找到过当前点的所有线段中最大的右端点转移过去,直到某个时刻新的点直接越过第二个区间,此时要设为第二个区间的右端点,以此类推,可以倍增加速在相邻两个区间之间跳跃的过程。

对于 \(c_i\in \{1,2\}\) 的情况就是边权为 \(1,2\) 的倍增,维护跳 \(2^k-1,2^k\) 步的结果,查询时同时维护跳 \(ans-1,ans\) 步的结果即可。

时间复杂度 \(\mathcal O((n+q)\log n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
int n,m,W,_,q,dsu[MAXN*2],bl[MAXN*2],L[MAXN*2],R[MAXN*2];
int id(int x,int y) { return (x-1)*W+y; }
int find(int x) { return dsu[x]^x?dsu[x]=find(dsu[x]):x; }
int h[MAXN],b[MAXN],mx[MAXN],f[MAXN][20],g[MAXN][20],l[MAXN],r[MAXN];
int nxt(int x) { return upper_bound(l+1,l+q+1,x)-l; }
int go(int x) { return x?min(h[x],r[nxt(x)]):r[1]; }
int solve() {cin>>q; vector <int> o;for(int i=0,x,y;i<q;++i) cin>>x>>y,o.push_back(bl[id(x,y)]);sort(o.begin(),o.end()),o.erase(unique(o.begin(),o.end()),o.end());if(o.size()==1) return 0;sort(o.begin(),o.end(),[&](int x,int y){ return R[x]^R[y]?R[x]<R[y]:L[x]>L[y]; }),q=0;for(int i:o) if(L[i]>l[q]) l[++q]=L[i],r[q]=R[i];l[q+1]=r[q+1]=n+1;if(mx[l[1]]<r[q]) return -1;int u=b[r[1]],v=0,w,z=1;while(v<=r[1]&&u<l[q]) w=max(go(v),b[go(u)]),v=u,u=w,++z;while(u<l[q]) {int t=nxt(v);for(int k=19;~k;--k) {w=max(f[u][k],g[h[v]][k]);if(w<l[t]) v=max(f[v][k],g[u][k]),u=w,z+=1<<k;}while(v<=r[t]&&u<l[q]) w=max(go(v),b[go(u)]),v=u,u=w,++z;}return z;
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>W>>_;iota(dsu+1,dsu+n*W+1,1);for(int i=1;i<=n;++i) for(int j=1;j<W;++j) {char c; cin>>c; if(c=='1') dsu[find(id(i,j))]=find(id(i,j+1));}for(int i=1;i<n;++i) for(int j=1;j<=W;++j) {char c; cin>>c; if(c=='1') dsu[find(id(i,j))]=find(id(i+1,j));}for(int i=1;i<=n;++i) for(int j=1;j<=W;++j) if(dsu[id(i,j)]==id(i,j)) {bl[id(i,j)]=++m,L[m]=R[m]=i;}for(int i=1;i<=n;++i) for(int j=1;j<=W;++j) {int x=bl[id(i,j)]=bl[find(id(i,j))]; L[x]=min(L[x],i),R[x]=max(R[x],i);}for(int i=1;i<=m;++i) h[L[i]]=max(h[L[i]],R[i]);for(int i=1;i<=n;++i) h[i]=max({h[i],h[i-1],i});for(int i=n;i>=1;--i) mx[i]=max(mx[h[i]],i);for(int i=1,z;i<=n;++i) cin>>z,b[i]=z>1?b[i-1]:i;for(int i=1;i<=n;++i) f[i][0]=max(i,b[h[i]]),g[i][0]=i;for(int k=1;k<20;++k) for(int i=1;i<=n;++i) {f[i][k]=max(f[f[i][k-1]][k-1],g[h[g[i][k-1]]][k-1]);g[i][k]=max(f[g[i][k-1]][k-1],g[f[i][k-1]][k-1]);}while(_--) cout<<solve()<<"\n";return 0;
}



*P. [P11921] 看护 (8)

Problem Link

先二分答案,注意到答案的分子 \(\le L\),分母 \(\le n\),直接排序后二分即可。

如果每个人入睡顺序确定时,只要贪心找最早可能的入睡时间即可。

如果暴力枚举下一个入睡的人,对每个 \(i\in[1,L],k\le [1,n]\) 维护前 \(i\) 个点最后一次空余点数 $\le k $ 的时刻,这样就能 \(\mathcal O(n)\) 算出最早的入睡时间。

然后对搜索进行剪枝,算出每个人最早的入睡时间,我们只要考虑这个时间最早和次早的两个人 \(x,y\)

考虑证明,如果答案中最早入睡的不是 \(x,y\),那么 \(x,y\) 在答案中较早入睡的一个调整成最早入睡的,不难证明这段时间内的另一个点一定没有入睡,所以调整后必定合法。

所以只要直接爆搜即可。

时间复杂度 \(\mathcal O((2^n+L)n\log L)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5,inf=1e9;
string a[18];
int n,m,c[MAXN],f[19][MAXN],d[18][MAXN],g[18][MAXN],X,Y,b[18];
bool dfs(int k,int S) {if(S==(1<<n)-1) return true;array <int,2> u={inf,-1},v={inf,-1};for(int i=0;i<n;++i) if(!(S>>i&1)) {int h=k?b[k-1]:0;for(int j=0;j<k;++j) {int r=b[j]+X;if(h<r) h=max(h,min(r,f[k-j+1][(r-1)/Y]*Y));}if(d[i][h/Y]*Y-h%Y<X) {if(h/Y==m||g[i][h/Y+1]<0) continue;h=g[i][h/Y+1]*Y;}if(u[0]>h) v=u,u={h,i};else if(v[0]>h) v={h,i};}if(~u[1]) {b[k]=u[0];if(dfs(k+1,S|1<<u[1])) return true;}if(~v[1]) {b[k]=v[0];if(dfs(k+1,S|1<<v[1])) return true;}return false;
}
bool chk() {for(int i=0;i<n;++i) {d[i][m]=0,g[i][m]=-1;for(int j=m-1;~j;--j) {d[i][j]=(a[i][j]=='X'||c[j]==1?0:d[i][j+1]+1);g[i][j]=(d[i][j]*Y>=X?j:g[i][j+1]);}}return dfs(0,0);
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=0;i<n;++i) {cin>>a[i];for(int j=0;j<m;++j) c[j]+=a[i][j]=='.';}if(count(c,c+m,0)) return cout<<"-1\n",0;for(int i=0;i<=n;++i) {for(int j=0,x=0;j<m;++j) f[i][j]=x=(c[j]<=i?j+1:x);}vector <array<int,2>> fc;fc.push_back({0,1});for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) if(__gcd(i,j)==1) fc.push_back({i,j});sort(fc.begin(),fc.end(),[&](auto x,auto y){ return x[0]*y[1]<x[1]*y[0]; });int z=0,l=1,r=fc.size()-1;while(l<=r) {int mid=(l+r)>>1; X=fc[mid][0],Y=fc[mid][1];if(chk()) z=mid,l=mid+1;else r=mid-1;}cout<<fc[z][0]<<"/"<<fc[z][1]<<"\n";return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/935890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025 年安防系统厂商最新推荐榜:弱电 / 智能 / 周界 / 监控等全品类服务商深度测评及选择指南

随着 AI、物联网技术与安防领域的深度融合,市场对智能安防系统的需求已从单一设备采购转向全场景解决方案。当前安防市场中,既有深耕多年的资深企业,也涌现出一批技术新锐品牌,但厂商资质、技术实力与服务能力的差…

献丑贴:Task.Run中foreach优化

有一个场景: 在Task.Run中循环执行N个任务,原来的写法:var task = Task.Run(async () =>{int i = 0;foreach (var item in tables){i++;await writefileAsync(namespace1, item, showProcess);}});_ = task.Cont…

完整教程:应用部署(后端)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

C# 定时任务 Quartz.NET 的使用

C# 定时任务 Quartz.NET 的使用一、定时任务的介绍 相信我们在生活中,大部分都会使用到定时任务去执行自定义的业务逻辑,如:每天早上8点钟发送一份汇总好的财经报告到指定人的邮箱;或者每周一5点30分钟自动执行下载…

2025.10.13——1橙

普及- P9752 [CSP-S 2023] 密码锁 虽然只是小模拟,但确实是真题,有点感觉。

WPF 通过RawInput获取系统全局触摸事件

WPF 通过RawInput获取系统全局触摸事件在做大屏或者平板的业务,或多或少会有监听触摸事件的需求。在WPF 上,监听自身应用的触摸事件是很简单的,可以监听 Windows的 Stylus、Touch、甚至是 Mouse的事件来实现业务逻辑…

基于高频电流探头的电磁兼容(EMI/EMC)测试与诊断技术方案

电磁兼容性(EMI/EMC)测试是确保电子设备在复杂电磁环境中可靠运行的重要环节。高频电流探头采用非侵入式测量方式,能够精准地捕捉电缆上的噪声电流,为诊断和解决电磁干扰问题提供可靠的数据支持。本文详细介绍了高…

Spring 事务、循环依赖连环问

Spring 事务 详情请查看:Spring 事务 Spring 事务实现方式有哪些? 事务就是一系列的操作原子执行。Spring事务机制主要包括声明式事务和编程式事务。编程式事务:通过编程的方式管理事务,这种方式带来了很大的灵活性…

20232327 2025-2026-1 《网络与系统攻防技术》实验一实验报告

20232327 2025-2026-1 《网络与系统攻防技术》实验一实验报告 1.实验内容 在本周的课程学习了缓冲区溢出和shellcode攻击的内容,以下是一些基本概念和解释:缓冲区:连续的一段存储空间; 缓冲区溢出攻击BOF(Buffer …

完整教程:OSPF LSA/ 路由种类

完整教程:OSPF LSA/ 路由种类2025-10-13 08:46 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importan…

单挑市面上所有监控设备模拟器/可能是最好用的监控模拟器/支持onvif和28181协议/虚拟监控/桌面转监控/任意源转监控设备

一、前言说明 自从发布了这个监控设备模拟器,本意其实是卖代码,可是受欢迎程度不是程序员开发者,而是一堆非开发人员,没想到这个一个毫不起眼的需求,在外行人看来是真切实际的需求,比如一些收银台,需要把收银软…

在Java 11中,如何处理被弃用的类或接口?

在Java 11中处理被弃用的类或接口时,核心原则是使用官方推荐的替代方案,避免依赖过时API以确保代码的兼容性和可维护性。以下是具体处理方式和示例: 1. 替换内部API类(sun.* 或 com.sun.* 包下的类) 这些类属于JD…

chmod只修改文件或者只修改目录权限

chmod和chmod -R目录常用于修改文件,文件夹权限。加上-R参数会迭代的修改子目录和文件的权限。如果只想修改文件的权限,文件夹的权限不受影响。则可以使用下面的方法:chmod 750 `find /a /b -type f`会修改文件夹/a…

每周资讯 | 腾讯《三角洲行动》周年庆登双榜TOP1;腾讯首款生活模拟游戏《粒粒的小人国》曝光 - 教程

每周资讯 | 腾讯《三角洲行动》周年庆登双榜TOP1;腾讯首款生活模拟游戏《粒粒的小人国》曝光 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: bloc…

.NET 自动依赖注入神器

在 .NET Core/Web 项目中,手动写一堆 services.AddScoped<...>、AddSingleton<...> 是否让你头大?今天给大家介绍一个神器——Injectio,帮你自动扫描并注册服务,减少重复代码,让你的依赖注入(DI)更…

NetDreamCTF WP - 指南

NetDreamCTF WP - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &quo…

[1134] Connecting to Land Insight SFTP and GIS Servers

[1134] Connecting to Land Insight SFTP and GIS ServersHi Sir Bing,Greetings!Please be informed of your user credentials to servers. Also attached is the Work Instruction and PPK to connect to servers f…

VLA技术论文阅读 - 详解

VLA技术论文阅读 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &qu…

深入解析:246-基于Django的美食菜谱数据分析推荐系统

深入解析:246-基于Django的美食菜谱数据分析推荐系统pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…

PhotoShop网页版在线为图片加文字,制作个性海报教程

生活中,我们总有需要给图片加文字、或是亲手做一张个性海报的时候。你是不是也觉得用专业Photoshop太复杂?别担心,现在只要打开浏览器,进入在线修图平台,零基础也能快速上手,轻松做出创意十足的作品! 一、为什么…