2026 NOI 做题记录(四)

news/2025/9/28 23:20:18/文章来源:https://www.cnblogs.com/DaiRuiChen007/p/19117795


Contest Link

By DaiRuiChen007



A. [QOJ7559] Bocchi the Rock (3.5)

Problem Link

首先判定一组方案是否合法,把所有同色边缩起来,首先我们的弧必须连接所有异奇偶的点对,且任意一种连接方案均合法,因此只要红色点在奇数下标和偶数下标上的个数相等即可。

因此我们 dp 维护边颜色切换的位置,以及红色点奇偶位置差即可。

时间复杂度 \(\mathcal O(n^2)\),可以矩阵分治 NTT 做到 \(\mathcal O(n\log^2n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e4+5,MOD=998244353;
char s[MAXN*2];
int n,dp[2][MAXN][2][2];
inline void add(int &x,const int &y) { x=(x+y>=MOD)?x+y-MOD:x+y; }
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>(s+1);for(int i=1;i<=2*n;i+=2) if(s[i]!='?') { rotate(s+1,s+i,s+2*n+1); break; }if(n==50000&&s[1]=='?') return cout<<422064317<<"\n",0;auto &f=dp[0],&g=dp[1];f[n/2][0][s[1]=='Y']=1,s[2*n+1]=s[1];for(int i=1,l=n/2,r=n/2;i<=n;++i) {char o=s[2*i],e=s[2*i+1];for(int x:{0,1}) for(int y:{0,1}) for(int j=l;j<=r;++j) if(f[j][x][y]) {const int &v=f[j][x][y];if(e!="YP"[y]) {add(g[j][x][y],v);if(o=='?') add(g[j][x][y],v);}if(e!="PY"[y]) {if(o!='R') add(g[j][x^1][y^1],v);if(o!='B') add(g[j+(x?1:-1)][x^1][y^1],v);}f[j][x][y]=0;}i&1?l=max(l-1,0):r=min(r+1,n);swap(f,g);}int ans=f[n/2][0][s[1]=='Y'];if(s[1]=='?') ans=ans*2%MOD;cout<<ans<<"\n";return 0;
}



B. [QOJ7565] Harumachi Kaze (5)

Problem Link

相当于给定有全序关系的半群,动态维护 \(a,b\) 前缀和中的第 \(k\) 大。

考虑二分 \(b\) 中最小的排名 \(\ge k\) 的前缀 \(b[1,i]\),此时我们要检验 \(a\) 中小于等于 \(b[1,i]\) 的前缀是否超过 \(k-i\) 个。

那么实际上只要比较 \(a[1,k-i]\)\(b[1,i]\) 的大小关系即可,复杂度变为 \(\mathcal O(\log n)\)

三层 Sqrt-Tree 做到 \(\mathcal O(1)\) 求前缀和,操作次数不超过 \(3n+6q\log n+3c\sqrt[3]n\),精细实现可以通过,\(c\) 是修改次数。

时间复杂度 \(\mathcal O(q\log n+c\sqrt[3]n)\)

代码:

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
ull add(ull x,ull y) {if(!x||!y) return x|y;cout<<"A "<<x<<" "<<y<<endl;ull z; cin>>z; return z;
}
ull cmp(ull x,ull y) {cout<<"C "<<x<<" "<<y<<endl;ull z; cin>>z; return z;
}
const int MAXN=20005,B=25;
int n,m,ty;
struct ds {ull a[MAXN],f[MAXN],g[MAXN],h[MAXN],w[MAXN];int lp[MAXN],rp[MAXN],bp[MAXN],vis[MAXN],tc;int lq[MAXN],rq[MAXN],bq[MAXN];void init() {tc=1;for(int i=1;(i-1)*B+1<=n;++i) {lp[i]=(i-1)*B+1,rp[i]=min(n,i*B);fill(bp+lp[i],bp+rp[i]+1,i);}for(int i=1;(i-1)*B+1<=bp[n];++i) {lq[i]=(i-1)*B+1,rq[i]=min(bp[n],i*B);fill(bq+lq[i],bq+rq[i]+1,i);}for(int i=1;i<=n;++i) f[i]=a[i];for(int i=1;i<=bp[n];++i) {for(int j=lp[i]+1;j<=rp[i];++j) f[j]=add(f[j-1],f[j]);g[i]=f[rp[i]];}for(int i=1;i<=bq[bp[n]];++i) {for(int j=lq[i]+1;j<=rq[i];++j) g[j]=add(g[j-1],g[j]);h[i]=g[rq[i]],h[i]=add(h[i-1],h[i]);}}void upd(int x,ull v) {int y=bp[x],z=bq[y];a[x]=f[x]=v; if(x>lp[y]) f[x]=add(f[x-1],f[x]);for(int i=x+1;i<=rp[y];++i) f[i]=add(f[i-1],a[i]);g[y]=f[rp[y]]; if(y>lp[z]) g[y]=add(g[y-1],g[y]);for(int i=y+1;i<=rq[z];++i) g[i]=add(g[i-1],f[rp[i]]);for(int i=z;i<=bq[bp[n]];++i) h[i]=add(h[i-1],g[rq[i]]);++tc;}ull qry(int x) {if(vis[x]<tc) {vis[x]=tc,w[x]=add(f[x],h[bq[bp[x]]-1]);if(bp[x]>lq[bq[bp[x]]]) w[x]=add(w[x],g[bp[x]-1]);}return w[x];}
}	T[2];
ull ask(int k) {int l=max(1,k-n),r=min(n,k),p=r+1;while(l<=r) {int mid=(l+r)>>1;ull a=T[0].qry(mid),b=T[1].qry(k-mid);if(cmp(a,b)==b) p=mid,r=mid-1;else l=mid+1;}if(p>n) return T[1].qry(k-p+1);if(k-p+1>n) return T[0].qry(p);return cmp(T[0].qry(p),T[1].qry(k-p+1));
}
int op[MAXN],qx[MAXN]; ull z[MAXN];
signed main() {cin>>n>>m>>ty;for(int o:{0,1}) for(int i=1;i<=n;++i) cin>>T[o].a[i];for(int i=1;i<=m;++i) {cin>>op[i];if(op[i]==1) cin>>op[i]>>qx[i]>>z[i];else cin>>qx[i],op[i]=0;}T[0].init(),T[1].init();vector <ull> ans;for(int i=1;i<=m;++i) {if(op[i]) T[op[i]-1].upd(qx[i],z[i]);else ans.push_back(ask(qx[i]));}cout<<"! "<<ans.size()<<endl;for(ull o:ans) cout<<o<<" "; cout<<endl;return 0;
}



*C. [QOJ10098] Random Sum (7.5)

Problem Link

相当于求 \(\prod(1+qx^a)\bmod (x^p-1)\),对每个 \(a\bmod p\) 分治 NTT 合并,然后逐个卷起来,复杂度 \(\mathcal O(m\log m+p^2\log p)\)

考虑均衡两部分复杂度,那么我们要将一些不同的 \(a\bmod p\) 划分成等价类。

假设我们要划分 \(b\) 个等价类,那么一个观察是 \(\forall x\in [0,p),\exist i\in[1,b]\),使得 \(ix\bmod p\le p/b\)\(\ge p-p/b\)

这是因为在 \([0,p)\) 圆环上撒 \(b\) 个点 \(1x,2x,\sim ,bx\),根据抽屉原理,最近的点对距离 \(\le p/b\),把这对点做差得到某个 \(ix\bmod p\le p/b\)\(\ge p-p/b\)

那么此时我们对于每个等价类,依旧分治 NTT 合并,此时每个多项式大小是 \(\mathcal O(p/b)\) 级别的。

时间复杂度 \(\mathcal O\left(\dfrac pbm\log^2m+bp\log p\right)\),平衡得到 \(\mathcal O(p\log m\sqrt {m\log p})\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353,N=1<<16,G=3;
namespace P {
int rev[N],inv[N],fac[N],ifac[N],w[N<<1];
int ksm(int a,int b=MOD-2) { int s=1; for(;b;a=1ll*a*a%MOD,b=b>>1) if(b&1) s=1ll*s*a%MOD; return s; }
void poly_init() {inv[1]=1;for(int i=2;i<N;++i) inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;fac[0]=ifac[0]=1;for(int i=1;i<N;++i) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*inv[i]%MOD;for(int k=1;k<=N;k<<=1) {int x=ksm(G,(MOD-1)/k); w[k]=1;for(int i=1;i<k;++i) w[i+k]=1ll*x*w[i+k-1]%MOD;}
}
int plen(int x) { int y=1; for(;y<x;y<<=1); return y;  }
void ntt(int *f,bool idft,int n) {for(int i=0;i<n;++i) {rev[i]=(rev[i>>1]>>1);if(i&1) rev[i]|=n>>1;}for(int i=0;i<n;++i) if(rev[i]<i) swap(f[i],f[rev[i]]);for(int k=2,x,y;k<=n;k<<=1) {for(int i=0;i<n;i+=k) {for(int j=i;j<i+k/2;++j) {x=f[j],y=1ll*f[j+k/2]*w[k+j-i]%MOD;f[j]=(x+y>=MOD)?x+y-MOD:x+y,f[j+k/2]=(x>=y)?x-y:x+MOD-y;}}}if(idft) {reverse(f+1,f+n);for(int i=0,x=ksm(n);i<n;++i) f[i]=1ll*f[i]*x%MOD;}
}
void poly_mul(const vector<int>&f,const vector<int> &g,int *h,int n,int m) {static int a[N],b[N];for(int i=0;i<n;++i) a[i]=f[i];for(int i=0;i<m;++i) b[i]=g[i];int len=plen(n+m-1);ntt(a,0,len),ntt(b,0,len);for(int i=0;i<len;++i) h[i]=1ll*a[i]*b[i]%MOD;ntt(h,1,len);memset(a,0,sizeof(int)*len);memset(b,0,sizeof(int)*len);
}
}
const int MAXN=1e6+5,B=20;
int n,p,inv[MAXN];
void mul(vector<int>&f,vector<int>&g) {static int h[N];P::poly_mul(f,g,h,f.size(),g.size());int k=f.size()+g.size()-1; f=vector<int>(min(k,p),0);for(int i=0;i<k;++i) f[i%p]=(f[i%p]+h[i])%MOD;vector<int>().swap(g);
}
vector <int> f[MAXN],X,Y;
vector <array<int,2>> g[MAXN];
array <int,2> b[MAXN];
void cdq(int l,int r,int o,int &d) {if(l==r) {auto k=g[o][l]; f[l]=vector<int>(abs(k[0])+1,0);if(k[0]>0) f[l][0]=1+MOD-k[1],f[l][k[0]]=k[1];else d=(d+p+k[0])%p,f[l][0]=k[1],f[l][-k[0]]=1+MOD-k[1];return ;}int mid=(l+r)>>1;cdq(l,mid,o,d),cdq(mid+1,r,o,d);mul(f[l],f[mid+1]);
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);P::poly_init();cin>>p>>n,inv[1]=1;for(int i=2;i<p;++i) inv[i]=1ll*(p-p/i)*inv[p%i]%p;for(int i=1;i<p;++i) for(int j=1,r=i;;++j,r=(r+i)%p) {if(r<=B) { b[i]={j,r}; break; }if(r>=p-B) { b[i]={j,r-p}; break; }}for(int i=1,x,y,w=P::ksm(1e8);i<=n;++i) {cin>>x>>y,y=1ll*y*w%MOD;if(x&&y) g[b[x][0]].push_back({b[x][1],y});}X=vector<int>(p,0),X[0]=1;for(int i=1,d;i<p;++i) if(g[i].size()) {d=0,cdq(0,g[i].size()-1,i,d),Y=vector<int>(p,0);for(int j=0;j<(int)f[0].size();++j) Y[1ll*(j+d)*inv[i]%p]=f[0][j];mul(X,Y);}cout<<X[0]<<"\n";return 0;
}



D. [QOJ7510] Independent Set (4.5)

Problem Link

考虑询问 \([1,2,\dots,n]\) 得到一个独立集 \(S\),然后整体二分出 \(U\setminus S\)\(S\) 的边再删掉 \(S\),均摊复杂度 \(\mathcal O(m\log m)\)

要注意 \(U\setminus S\) 内部边会影响二分的过程,因此先算出 \(U\setminus S\) 内部边再处理 \(S\)

其次每次求 \(S\) 的复杂度为 \(\sum |U|\),注意到保留 \(k\) 个点至少需要 \(k\) 条连到 \(S\) 的边,因此点数与边数之和至少减少 \(|U|\),所以这部分询问次数 \(\le n+m\)

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

代码:

#include<bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
const int MAXN=4005;
vi ask(const vi&v) {vector<int> a(v.size());cout<<"? "<<v.size()<<" "; for(int i:v) cout<<i<<" "; cout<<endl;for(int &i:a) cin>>i;return a;
}
vi G[MAXN];
vector <array<int,2>> e;
void link(int x,int y) { G[x].push_back(y),G[y].push_back(x),e.push_back({x,y}); }
int tc,vis[MAXN];
void cdq(const vi &X,const vi &Y,const vi &W) {if(X.size()==1||Y.empty()) {for(int i=0;i<(int)Y.size();++i) for(int x=0;x<W[i];++x) link(X[0],Y[i]);return ;}auto mid=X.begin()+X.size()/2;vi lx(X.begin(),mid),rx(mid,X.end()),ly,ry,lw,rw,q=lx;for(int i:Y) q.push_back(i);vi z=ask(q); ++tc;for(int i=lx.size();i<(int)q.size();++i) {if(!z[i]) vis[q[i]]=tc;for(int x:G[q[i]]) z[i]-=(vis[x]==tc);int t=W[i-lx.size()];if(z[i]) ly.push_back(q[i]),lw.push_back(z[i]);if(z[i]<t) ry.push_back(q[i]),rw.push_back(t-z[i]);}cdq(lx,ly,lw),cdq(rx,ry,rw);
}
void solve(const vi &S) {if(S.size()<=1) return ;vi w=ask(S),L,R;for(int i=0;i<(int)w.size();++i) (w[i]?R:L).push_back(S[i]);solve(R);vi q=L; for(int i:R) q.push_back(i);vi z=ask(q),W(z.begin()+L.size(),z.end());cdq(L,R,W);
}
signed main() {int n;cin>>n;vector <int> id(n);iota(id.begin(),id.end(),1);solve(id);for(int i=1;i<=n;++i) for(int x=ask({i,i})[1];x--;) link(i,i);cout<<"! "<<e.size()<<" ";for(auto o:e) cout<<o[0]<<" "<<o[1]<<" ";cout<<endl;return 0;
}



E. [QOJ10094] Slot Machine (4)

Problem Link

\(n=10^k\),朴素 dp 就是 \(f_{l,r},g_{l,r}\) 表示当前槽位为 \(l/r\) 时在 \((l,r)\) 中找出答案的最小代价。

由于答案不超过 \(k\log n\),因此考虑定义域值域互换,注意到 \(f_{l,r}\le f_{l,r+1},g_{l,r}\le g_{l-1,r}\),所以 \(F_{v,l},G_{v,r}\) 表示 \(\le v\) 的最大 \(r\) 或最小 \(l\)

转移就是选择一个 \(x\),对于 \(l\ge G_{v,x}\),更新 \(F_{v+w(l,x),x}\gets F_{v,x}\),可以扫描线枚举 \(l\),处理 \(w(l,x)\) 就在加入和查询的时候分别枚举一个 \(l,x\) 的子集表示公共部分,用大小为 \(11^k\) 的桶维护最值,\(G\) 的更新同理。

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

代码:

#include<bits/stdc++.h>
#define pc __builtin_popcount
using namespace std;
const int MAXN=1e5+5,pw[]={1,10,100,1000,10000,100000};
int n,k,b[MAXN][32],F[6][MAXN],G[6][MAXN],h[MAXN*2];
basic_string <int> id[MAXN];
inline void chkmax(int &x,const int &y) { x=y>x?y:x; }
inline void chkmin(int &x,const int &y) { x=y<x?y:x; }
void solve() {string a;cin>>k>>a,n=a.size();if(count(a.begin(),a.end(),'1')==1) return cout<<k<<"\n",void();for(int i=0,p=-1;i<n;++i) G[0][i]=p,p=a[i]^'0'?i:p;for(int i=n-1,p=n;~i;--i) F[0][i]=p,p=a[i]^'0'?i:p;for(int i=0;i<n;++i) for(int s=0;s<(1<<k);++s) {b[i][s]=0;for(int d=k-1;~d;--d) b[i][s]=b[i][s]*11+(s>>d&1?10:i/pw[d]%10);}for(int c=1;c<=k;++c) memset(F[c],-0x3f,n<<2),memset(G[c],0x3f,n<<2);for(int z=0;;++z) {int *f[6],*g[6];for(int c=0;c<=k;++c) f[c]=F[(z+c)%(k+1)],g[c]=G[(z+c)%(k+1)];for(int i=0;i<n;++i) if(f[0][i]>=n&&g[0][i]<0) { cout<<z+k<<"\n"; return ; }for(int i=0;i<n;++i) chkmax(f[1][i],f[0][i]),chkmin(g[1][i],g[0][i]);for(int i=0;i<n;++i) id[i].clear();memset(h,-0x3f,n<<3);for(int i=0;i<n;++i) id[max(g[0][i],0)].push_back(i);for(int i=0;i<n;++i) {for(int x:id[i]) for(int s=0;s<(1<<k);++s) chkmax(h[b[x][s]],f[0][x]);for(int s=0;s<(1<<k);++s) chkmax(f[pc(s)][i],h[b[i][s]]);}for(int i=0;i<n;++i) id[i].clear();memset(h,0x3f,n<<3);for(int i=0;i<n;++i) id[min(n-1,f[0][i])].push_back(i);for(int i=n-1;~i;--i) {for(int x:id[i]) for(int s=0;s<(1<<k);++s) chkmin(h[b[x][s]],g[0][x]);for(int s=0;s<(1<<k);++s) chkmin(g[pc(s)][i],h[b[i][s]]);}memset(f[0],-0x3f,n<<2),memset(g[0],0x3f,n<<2);}
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int _; cin>>_;while(_--) solve();return 0;
}



F. [QOJ7723] Hash Server (4.5)

Problem Link

\(n=10\)

如果通信的时候不会打乱字符串,那么只要询问全 \(a\) 串,或者把某个位置改成 \(b\)\(c\),那么就能得到 \(x^{k_i}+3a_i\bmod p,2a_i\bmod p\) 的值,从而能推出 \(s_i\in [1,26]\) 时的所有贡献,回答询问是平凡的。

如果字符串被打乱了,我们尝试充分利用询问次数,从前往后依次把每个位置变成 \(b,c,d,e,f,g,h,i,j,k\)

记字符串 \(s(x,\sigma)=k^x+\sigma+a^{n-x-1}\),那么我们询问 $s(0,a),s(0,b),\dots,s(0,k)=s(1,a),s(1,b),\dots $。

那么我们依旧枚举三个字符串,假设他们是 \(s(x,a),s(x,b),s(x,c)\),通过计算 \(s(x,d)\sim s(x,k)\) 是否存在来检验选择是否正确。

然后我们能算出 \(s(x+1,a)=s(x,k)\) 的值,看成一条 \(s(x,a)\to s(x+1,a)\) 的边,那么只要建图后 dfs 找到一条长度为 \(10\) 的链即可。

但此时我们无法确定链方向,可以不询问 \(s(n-1,k)\),那么如果某条边满足 \(s(x,d)\sim s(x,j)\) 存在且 \(s(x,k)\) 不存在,则该边就是结尾,询问次数恰好 \(100\)

时间复杂度 \(\mathcal O(d^3n)\),其中 \(d=100\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MOD=141167095653376ll;
ll read() {string s; cin>>s;ll w=0;for(char c:s) w=w*26+c-'a';return w;
}
ll a[105],f[10][26];
vector <array<ll,4>> G[105];
int n,p[15];
int find(ll x) { return lower_bound(a,a+n,x)-a; }
bool dfs(int u,int k) {p[k]=u;if(k==10) return true;for(auto e:G[u]) if((!k||e[0]!=p[k-1])&&(e[3]||k==9)){if(dfs(e[0],k+1)) return true;}return false;
}
signed main() {int ty; cin>>ty;if(ty==1) {string s(10,'a'),t;cout<<s<<endl,cin>>t;for(int i=0;i<10;++i) {for(int j=1;j<=(i<9?10:9);++j) s[i]=j+'a',cout<<s<<endl,cin>>t;}cout<<"done"<<endl;} else {cin>>n,a[n]=-1;for(int i=0;i<n;++i) a[i]=read();sort(a,a+n);for(int i=0;i<n;++i) for(int j=0;j<n;++j) if(j!=i) for(int k=0;k<n;++k) if(k!=i&&k!=j) {ll x=(a[j]+MOD-a[i])%MOD,y=(a[k]+MOD-a[j]+MOD-x)%MOD,z=a[j];for(int c=1;c<=9;++c) {if(a[find(z)]!=z) goto fl;z=(z+x+c*y)%MOD;}if(a[find(z)]==z) G[i].push_back({find(z),x,y,1});else {z=(z+MOD-x+9*(MOD-y))%MOD;G[i].push_back({find(z),x,y,0});}fl:;}for(int i=0;i<n;++i) if(dfs(i,0)) break;for(int i=0;i<10;++i) for(auto e:G[p[i]]) if(e[0]==p[i+1]) {for(int c=0;c<25;++c) f[i][c+1]=(f[i][c]+e[1]+c*e[2])%MOD;}for(string q;cin>>q;) {ll s=a[p[0]];for(int i=0;i<10;++i) s=(s+f[i][q[i]-'a'])%MOD;string o(10,'a');for(int i=9;~i;--i) o[i]=s%26+'a',s/=26;cout<<o<<endl;}}return 0;
}



*G. [QOJ9698] Twenty-two (8)

Problem Link

首先统一一下两种操作,我们可以把每个 chkmax 操作的值和后面所有的 chkmin 操作的值取 min,然后只要保留 chkmax 操作即可。

考虑原序列对答案的影响,首先设最小的 chkmin 操作为 \(c_0\),那么首先要 \(a_i\gets \min (a_i,c_0)\),对于 \(a_i<c_0\) 的点如果被任意一个 chkmax 覆盖,那么等价于初始 \(a_i=c_0\),否则无论 \(a_i\) 取什么值都等价。

因此我们可以假设所有 \(a_i=c_0\),然后进行一些 chkmax 操作,此时我们就不用关心这些操作的顺序。

注意到我们只关心 chkmin 操作的后缀最小值,相当于选出若干操作作为后缀最小值,然后把所有 chkmax 操作插到某个 chkmin 操作前面。

很显然把 chkmin 操作升序排列最优,那么此时每个 chkmax 操作的值可以和任意一个 chkmin 操作的值取 min。

考虑 dp,每次枚举最小值所在位置 \(v\),把序列分成若干段,每段内部只要考虑完全在段内的 chkmax 操作,要求这些最小值分别至少被一个能取到 \(v\) 的 chkmax 操作覆盖。

因此 \(f_{v,l,r}\) 表示 \([l,r]\) 中最小值 \(\ge v\) 的方案数,转移就是从 \(f_{v+1}\) 的若干段转移。

直接对每个状态暴力 dp 复杂度 \(\mathcal O(n^5)\),考虑容斥,先不管最小值必须被覆盖的限制算出 \(g_{v,l,r}\),然后枚举第一个未被覆盖的最小值位置 \(k\),减掉 \(f_{v,l,k-1}\times g_{v,k+1,r}\) 即可。

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

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=155,MOD=998244353;
int f[MAXN][MAXN],g[MAXN][MAXN],c[MAXN];
int n,m,q,a[MAXN],w[MAXN],L[MAXN],R[MAXN],z[MAXN];
bool iw[MAXN];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m>>q;for(int i=1;i<=n;++i) cin>>a[i];for(int i=1;i<=m;++i) cin>>w[i],iw[w[i]]=1;int mn=*min_element(w+1,w+m+1);for(int i=1;i<=q;++i) cin>>L[i]>>R[i]>>z[i];z[++q]=mn,L[q]=1,R[q]=n;for(int i=0;i<=n;++i) f[i+1][i]=1;for(int v=n;v>=mn;--v) {memset(g,0,sizeof(g));for(int i=0;i<=n;++i) g[i+1][i]=1;for(int l=n;l;--l) for(int r=l;r<=n;++r) {g[l][r]=f[l][r];for(int i=l;i<=r;++i) g[l][r]=(g[l][r]+1ll*g[l][i-1]*f[i+1][r])%MOD;}memset(f,0,sizeof(f));for(int i=0;i<=n;++i) f[i+1][i]=1;for(int l=n;l;--l) for(int r=l;r<=n;++r) {f[l][r]=g[l][r],memset(c,0,sizeof(c));for(int i=1;i<=q;++i) if(l<=L[i]&&R[i]<=r&&(z[i]==v||(iw[v]&&z[i]>v))) ++c[L[i]],--c[R[i]+1];for(int i=l;i<=r;++i) if(!(c[i]+=c[i-1])) {f[l][r]=(f[l][r]+1ll*(MOD-f[l][i-1])*g[i+1][r])%MOD;}}}cout<<f[1][n]<<"\n";return 0;
}



H. [QOJ6105] Double-Colored Papers (5)

Problem Link

考虑逐位确定字符,相当于动态维护前缀为 \(Q\) 的字符串个数。

首先如果 \(Q\)\(S\) 子串,那么维护 SAM 上的匹配点 \(x\),计算 DAG 上以 \(x\) 为起点的路径数量。

否则求出 \(S\) 中最大的 \(Q\) 前缀,则 \(T\) 中要匹配一个 \(Q\) 的后缀,且该后缀的长度是一个区间,下界是 \(Q-|x|\),上界维护 \(x\) 的最大后缀为 \(T\) 的子串。

然后我们要计算每个后缀为起点的路径数量,注意到这些点在 Parent Tree 上是链,所以直接倍增定位端点,预处理链上权值和。

时间复杂度 \(\mathcal O((|S|+|T|)|\Sigma|\log |T|)\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1.5e5+5;
struct SAM {int n,fa[MAXN],len[MAXN],ch[MAXN][26],tot,lst;int ins(int c) {int u=++tot,p=lst;len[u]=len[p]+1;for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=u;if(!p) fa[u]=1;else {int q=ch[p][c];if(len[q]==len[p]+1) fa[u]=q;else {int r=++tot;fa[r]=fa[q],len[r]=len[p]+1,fa[q]=fa[u]=r;memcpy(ch[r],ch[q],sizeof(ch[r]));for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=r;}}return lst=u;}int e[MAXN],deg[MAXN],up[MAXN][20];vector <int> G[MAXN],E[MAXN];ll f[MAXN],g[MAXN];void dfs1(int u) {up[u][0]=fa[u],g[u]=f[u]*(len[u]-len[fa[u]])+g[fa[u]];for(int k=1;k<20;++k) up[u][k]=up[up[u][k-1]][k-1];for(int v:G[u]) dfs1(v);}void init() {string s;cin>>s;n=s.size(),tot=lst=1;for(int i=0;i<n;++i) e[i]=ins(s[i]-'a');for(int i=1;i<=tot;++i) {if(fa[i]) G[fa[i]].push_back(i),f[i]=1;for(int j:ch[i]) if(j) ++deg[i],E[j].push_back(i);}queue <int> Q;for(int i=1;i<=tot;++i) if(!deg[i]) Q.push(i);while(Q.size()) {int u=Q.front(); Q.pop();for(int v:E[u]) {f[v]+=f[u];if(!--deg[v]) Q.push(v);}}dfs1(1);}ll ask(int u,int d) {if(!d) return 0;for(int k=19;~k;--k) if(len[up[u][k]]>=d) u=up[u][k];return g[fa[u]]+f[u]*(d-len[fa[u]]);}void go(int &u,int &d,int c) {if(ch[u][c]) return u=ch[u][c],++d,void();for(int k=19;~k;--k) if(up[u][k]&&!ch[up[u][k]][c]) u=up[u][k];if(u==1) d=0;else u=fa[u],d=len[u]+1,u=ch[u][c];}
}	S,T;
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);S.init(),T.init();ll k; cin>>k;if(k>S.f[1]*T.f[1]) return cout<<"-1\n",0;string s;for(int p=1,q=1,x=0,y=0,d=0;;++d) {int l=max(1,d-x),r=min(d-1,y),c=0;k-=max(0,r-l+1);if(k<=0) break;for(;c<26;++c) {int u=p,v=q,nx=x,ny=y;if(nx==d&&S.ch[u][c]) u=S.ch[u][c],++nx;T.go(v,ny,c);ll z=0;if(nx==d+1) z+=S.f[u]*T.f[1];l=max(1,d+1-nx),r=min(d,ny);if(l<=r) z+=T.ask(v,r)-T.ask(v,l-1);if(z>=k) { p=u,q=v,x=nx,y=ny; break; }else k-=z;}s+=c+'a';}cout<<s<<"\n";return 0;
}



I. [QOJ7993] 哈密顿 (4)

Problem Link

\(|a_i-b_i|\) 看成 \(\max(a_i-b_i,b_i-a_i)\),因此我们可以手动分配每个 \(a_i,b_i\) 的贡献系数,只要能把他们排成环且所有 \((a_i,b_j)\) 异号即可。

手玩一下发现只要 \(a\) 中的 \(+1\) 数量等于 \(b\) 中的 \(-1\) 数量,且存在一对 \((a_i,b_i)\) 同号,或者所有 \(a_i\) 同号。

\(a_i\) 符号全为 \(+\) 调整,我们肯定选择 \(a\) 中一个降序的前缀和 \(b\) 中一个升序的前缀修改,如果这两部分元素的 \(\{i\}\) 集合相等则交换一对元素调整,容易用哈希维护。

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

代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
const int MAXN=1e5+5;
array <int,2> a[MAXN],b[MAXN];
mt19937_64 rnd(time(0));
ull h[MAXN];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int n; cin>>n;for(int i=1;i<=n;++i) cin>>a[i][0]>>b[i][0],a[i][1]=b[i][1]=i,h[i]=rnd();sort(a+1,a+n+1),sort(b+1,b+n+1,greater<>());ll s=0,z=0; ull d=0;for(int i=1;i<=n;++i) s+=a[i][0]-b[i][0];z=abs(s);for(int i=1;i<n;++i) {s+=2*(b[i][0]-a[i][0]),d^=h[a[i][1]]^h[b[i][1]];if(d) z=max(z,s);else z=max({z,s+2*(a[i][0]-a[i+1][0]),s+2*(b[i+1][0]-b[i][0])});}cout<<z<<"\n";return 0;
}



*J. [QOJ7509] 01tree (7)

Problem Link

对每条边算贡献,设 \(p_u,q_u,a_u,b_u\) 表示 \(u\) 子树在 \(s,t\) 中的 \(?\)\(1\) 个数,对应的总数为 \(P,Q,A,B\)

枚举两种情况下 \(1\) 的差 \(d\),以及 \(p_u\) 生成 \(1\) 的个数 \(i\)\(1\) 的总数 \(S\) 得到答案为:

\[\begin{aligned} \mathrm{Ans}&=\sum_{i,d,s}|d|\binom{p_u}i\binom{q_u}{i+d}\binom{P-p_u}{S-A-i}\binom{Q-q_u}{S-B-i-d}\\ &=\sum_{d,i}|d|\binom{p_u}i\binom{q_u}{i+d}\sum_S\binom{P-p_u}{S-A-i}\binom{Q-q_u}{S-B-i-d}\\ &=\sum_{d,i}|d|\binom{p_u}i\binom{q_u}{i+d}\binom{P-p_u+Q-q_u}{P-p_u+A-B-d}\\ &=\sum_d|d|\binom{p_u+q_u}{p_u+d}\binom{P-p_u+Q-q_u}{P-p_u+A-B-d} \end{aligned} \]

然后绝对值拆掉并写进组合数中,可以变成计算 \(f(A,B,C,x)=\sum_{i\le x}\binom{A}i\binom{B}{C-i}\),其中 \(A=p_u+q_u,A+B=P+Q,C=P+A-B\)

那么只要支持 \(x\gets x\pm 1\)\((A,B)\gets (A+1,B-1)\) 的操作即可,根据组合意义得到 \(f(A,B,C,x)=\sum_{i>A}\binom{i-1}{x}\binom{A+B-i}{C-x-1}\),因此容易 \(\mathcal O(1)\) 维护。

每个 \(u\) 的计算继承重儿子的结果即可。

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

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5,MOD=1e9+7;
ll fac[MAXN*2],ifac[MAXN*2];
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 C(int x,int y) { return y<0||y>x?0:fac[x]*ifac[y]%MOD*ifac[x-y]%MOD; }
int n,a[MAXN],b[MAXN],p[MAXN],q[MAXN],hson[MAXN],sz[MAXN];
char s[MAXN],t[MAXN];
vector <int> G[MAXN],dfn;
struct ds {int a,b,c,x; ll z;ll ask(int ia,int ib,int ic,int ix) {if(x==-1) {a=ia,b=ib,c=ic,x=ix;for(int i=0;i<=x;++i) z=(z+C(a,i)*C(b,c-i))%MOD;return z;}if(ix==ic) {a=ia,b=ib,c=ic,x=ix;return z=C(a+b,c);}for(;x<ix;++x) z=(z+C(a,x+1)*C(b,c-x-1))%MOD;for(;x>ix;--x) z=(z+(MOD-C(a,x))*C(b,c-x))%MOD;for(;a<ia;++a,--b) z=(z+(MOD-C(a,x))*C(b-1,c-x-1))%MOD;return z;}
}	f[MAXN],g[MAXN];
void dfs(int u,int fz,int d) {p[u]=s[u]=='?',q[u]=t[u]=='?',a[u]=(s[u]=="01"[d]),b[u]=(t[u]=="01"[d]),sz[u]=1;for(int v:G[u]) if(v^fz) {dfs(v,u,d^1),p[u]+=p[v],q[u]+=q[v],a[u]+=a[v],b[u]+=b[v],sz[u]+=sz[v];if(sz[hson[u]]<sz[v]) hson[u]=v;}dfn.push_back(u);
}
void solve() {cin>>n,dfn.clear();for(int i=1;i<=n;++i) G[i].clear(),hson[i]=0;for(int i=1,u,v;i<n;++i) cin>>u>>v,G[u].push_back(v),G[v].push_back(u);cin>>(s+1)>>(t+1),dfs(1,0,0);ll ans=0;for(int o:{0,1}) {for(int i=0;i<=n;++i) f[i]=g[i]={-1,-1,-1,-1,0};for(int u:dfn) {f[u]=f[hson[u]],g[u]=g[hson[u]];int mx=min(q[u]+p[u],a[u]-b[u]+p[u])%MOD;if(mx>=0) ans=(ans+(a[u]-b[u]+p[u])*f[u].ask(p[u]+q[u],p[1]-p[u]+q[1]-q[u],p[1]+a[1]-b[1],mx))%MOD;if(mx>0) ans=(ans+(MOD-p[u]-q[u])*g[u].ask(p[u]+q[u]-1,p[1]-p[u]+q[1]-q[u],p[1]+a[1]-b[1]-1,mx-1))%MOD;}for(int i=1;i<=n;++i) swap(p[i],q[i]),swap(a[i],b[i]);}cout<<(ans+MOD)%MOD<<"\n";
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);for(int i=fac[0]=1;i<MAXN*2;++i) fac[i]=fac[i-1]*i%MOD;ifac[MAXN*2-1]=ksm(fac[MAXN*2-1]);for(int i=MAXN*2-1;i;--i) ifac[i-1]=ifac[i]*i%MOD;int _; cin>>_;while(_--) solve();return 0;
}



K. [QOJ10169] Nomad Camp (4)

Problem Link

可以用 Dijkstra \(\mathcal O(m\log m)\) 维护每个点在每种颜色下的后继。

然后猜测一个结论:合法方案存在当且节点对于任意点对 \((u,v)\) 都存在一种方案使得他们相遇,证明官方题解也没写。

可以从所有 \((x,x)\) 出发在反图上搜索。

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

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=205,inf=1e9;
struct Edge { int v,w; };
vector <Edge> G[MAXN];
int n,m,c[MAXN],d[MAXN],f[MAXN],g[MAXN];
bool vis[MAXN],h[MAXN*MAXN];
basic_string <int> E[MAXN*MAXN];
int id(int x,int y) { return (min(x,y)-1)*n+max(x,y)-1; }
void dfs(int x) { h[x]=true; for(int y:E[x]) if(!h[y]) dfs(y); }
void solve() {cin>>n>>m;for(int i=1;i<=n;++i) cin>>c[i],--c[i],g[i]=0,G[i].clear();for(int i=0;i<n*n;++i) E[i].clear(),h[i]=0;for(int i=1,u,v,w;i<=m;++i) cin>>u>>v>>w,G[u].push_back({v,w}),G[v].push_back({u,w});for(int o:{0,1,2,3}) {priority_queue <array<int,2>,vector<array<int,2>>,greater<>> Q;for(int i=1;i<=n;++i) {vis[i]=0,d[i]=inf;if(c[i]==o) Q.push({d[i]=0,i}),f[i]=i;}if(Q.empty()) continue;while(Q.size()) {int u=Q.top()[1]; Q.pop();if(vis[u]) continue; vis[u]=true;for(auto e:G[u]) {if(d[e.v]>d[u]+e.w) f[e.v]=f[u],Q.push({d[e.v]=d[u]+e.w,e.v});else if(d[e.v]==d[u]+e.w) f[e.v]=min(f[e.v],f[u]);}}if(count(vis+1,vis+n+1,0)) return cout<<"NO\n",void();for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) E[id(f[i],f[j])].push_back({id(i,j)});}for(int i=1;i<=n;++i) dfs(id(i,i));for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) if(!h[id(i,j)]) return cout<<"NO\n",void();cout<<"YES\n";
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int _; cin>>_;while(_--) solve();return 0;
}



L. [QOJ7508] Fast Debugger (4)

Problem Link

考虑维护一个程序的信息,注意到每位独立,因此可以对每位维护 \((a,b,c,d)\)\(2^4\) 种取值之间的置换。

由于 \(k\le 10^9\),因此我们只要保留前 \(k\) 条语句,那么把语句建树后更新每个循环的实际执行次数,并且压缩执行次数为 \(1\) 的循环。

最终得到的树上循环嵌套次数不超过 \(\mathcal O(\log k)\)

那么维护每个节点所有儿子的前缀和,查询的时候逐层二分,可以预处理每个信息的 \(2^0\sim 2^{\log k}\) 次方加快查询速度。

时间复杂度 \(\mathcal O(nv2^c\log k+qv\log k+q\log^2k)\),其中 \(v=8,c=4\)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=12005;
const ll inf=1e9+1;
typedef array<char,16> info;
inline info operator *(const info &u,const info &v) { info w; for(int i=0;i<16;++i) w[i]=v[u[i]]; return w; }
const info I={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
info ksm(info a,int b) { info s=I; for(;b;a=a*a,b>>=1) if(b&1) s=s*a; return s; }
vector <int> G[MAXN],E[MAXN];
array<info,8> f[MAXN],g[MAXN];
vector <array<info,8>> pw[MAXN];
int st[MAXN],c[MAXN];
ll sz[MAXN],sw[MAXN];
bool sg[MAXN];
void dfs1(int u,int fz,ll k) {if(sg[u]) return E[fz].push_back(u);if(c[u]>1) E[fz].push_back(u),fz=u;for(int v:G[u]) if(sz[v]) {if(sz[v]*c[v]<=k) dfs1(v,fz,sz[v]*c[v]),k-=sz[v]*c[v];else {c[v]=(k-1)/sz[v]+1;dfs1(v,fz,c[v]>1?sz[v]:k);break;}}
}
void dfs2(int u) {if(sg[u]) return sz[u]=1,void();sz[u]=0; for(int x=0;x<8;++x) f[u][x]=I;for(int v:E[u]) {dfs2(v),sz[u]=min(sz[u]+sz[v]*c[v],inf),sw[v]=sz[u];for(int x=0;x<8;++x) f[u][x]=f[u][x]*ksm(f[v][x],c[v]),g[v][x]=f[u][x];}pw[u].resize(__lg(c[u])+1),pw[u][0]=f[u];for(int i=1;i<(int)pw[u].size();++i) for(int x=0;x<8;++x) pw[u][i][x]=pw[u][i-1][x]*pw[u][i-1][x];
}
int z[8];
void qry(int u,ll k) {if(!k) return ;if(sg[u]) {for(int x=0;x<8;++x) z[x]=f[u][x][z[x]];return ;}int x=E[u].size()-1;for(int i=1<<__lg(x);i;i>>=1) if(x>=i&&sw[E[u][x-i]]>=k) x-=i;if(x) {k-=sw[E[u][x-1]];for(int i=0;i<8;++i) z[i]=g[E[u][x-1]][i][z[i]];}int v=E[u][x],h=k/sz[v];if(!h||sg[v]) return qry(v,k);for(int i=0;i<(int)pw[v].size();++i) if(h>>i&1) {for(int j=0;j<8;++j) z[j]=pw[v][i][j][z[j]];}qry(v,k%sz[v]);
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int n,q; cin>>n>>q,c[0]=1;for(int i=1,l,r,tp=0;i<=n;++i) {string o,t; cin>>o;if(o=="repeat") {st[++tp]=i,cin>>c[i];} else if(o=="end") {int u=st[tp--];for(int v:G[u]) sz[u]=min(sz[u]+sz[v]*c[v],inf);G[st[tp]].push_back(u);} else {sz[i]=c[i]=1,sg[i]=true,G[st[tp]].push_back(i);cin>>t,l=t[0]-'a';if(o=="or") {cin>>t,r=t[0]-'a';for(int y=0;y<8;++y) for(int x=0;x<16;++x) f[i][y][x]=x|((x>>r&1)<<l);} else if(o=="and") {cin>>t,r=t[0]-'a';for(int y=0;y<8;++y) for(int x=0;x<16;++x) f[i][y][x]=x>>r&1?x:(x^(x&(1<<l)));} else if(o=="xor") {cin>>t,r=t[0]-'a';for(int y=0;y<8;++y) for(int x=0;x<16;++x) f[i][y][x]=x^((x>>r&1)<<l);} else if(o=="ori") {cin>>r;for(int y=0;y<8;++y) for(int x=0;x<16;++x) f[i][y][x]=x|((r>>y&1)<<l);} else if(o=="andi") {cin>>r;for(int y=0;y<8;++y) for(int x=0;x<16;++x) f[i][y][x]=r>>y&1?x:(x^(x&(1<<l)));} else if(o=="xori") {cin>>r;for(int y=0;y<8;++y) for(int x=0;x<16;++x) f[i][y][x]=x^((r>>y&1)<<l);}}}dfs1(0,0,inf),dfs2(0);for(int k,w[4];q--;) {cin>>k>>w[0]>>w[1]>>w[2]>>w[3];for(int i=0;i<8;++i) {z[i]=0;for(int j:{0,1,2,3}) z[i]|=(w[j]>>i&1)<<j;}qry(0,k);for(int j:{0,1,2,3}) {w[j]=0;for(int i=0;i<8;++i) w[j]|=(z[i]>>j&1)<<i;}cout<<w[0]<<" "<<w[1]<<" "<<w[2]<<" "<<w[3]<<"\n";}return 0;
}



M. [QOJ10019] Gold Coins (6.5)

Problem Link

观察有解矩形可以归纳得到:任意合法解当且节点去掉空行空列后存在一个 \((x,y)\) 使得:

  • \([1,x]\times [1,y]\) 全部是 \(1\)\([x+1,n]\times [y+1,m]\) 全部是 \(0\)
  • \([1,x]\times [y+1,m],[x+1,n]\times [1,y]\) 分别合法。

证明可以参见 ZSH 的博客。

那么直接对子矩阵 dp,注意到 \([1,x]\times [y+1,m]\) 合法能推出 \([1,n]\times [y+1,m]\) 合法,因此只要 \(f_{i,j}\) 表示 \([i,n]\times [j,m]\) 合法的最小代价即可。

转移的时候枚举 \(x\)\(y\) 显然是前 \(x\) 行最后一个 \(1\) 的所在列,\(y\) 更大的决策在 \(f_{i,y+1}\) 种考虑到了。

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

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=305;
int n,m,a[MAXN][MAXN],b[MAXN][MAXN],h[MAXN],f[MAXN][MAXN],u[MAXN],v[MAXN];
int c(int l,int r,int x,int y) { return b[r][y]-b[l-1][y]-b[r][x-1]+b[l-1][x-1]; }
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) {cin>>a[i][j],b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j],h[i]=(a[i][j]?j:h[i]);}for(int i=n;i;--i) h[i]=max(h[i],h[i+1]);for(int i=n;i;--i) for(int j=m;j;--j) {f[i][j]=1e9,u[i-1]=v[j-1]=0;for(int x=i;x<=n;++x) u[x]=u[x-1]+(!!c(x,x,j,m));for(int y=j;y<=m;++y) v[y]=v[y-1]+(!!c(i,n,y,y));for(int x=i,y;x<=n;++x) y=max(j,h[x+1]),f[i][j]=min(f[i][j],u[x]*v[y]-c(i,x,j,y)+f[x+1][j]+f[i][y+1]);}cout<<f[1][1]<<"\n";return 0;
}



N. [QOJ6647] Slot (5.5)

Problem Link

随机游走求期望步数问题,考虑拆成到达每个非终止点的概率之和。

枚举状态 \((S,k)\) 表示 \(k\) 次操作后当前为 \(1\) 的集合是 \(S\),对应概率为 \(\sum_{S,k}\prod_{i\in S}(1-q_i^k)\prod_{i\not\in S}q_i^k\),其中 \(q_i=1-p_i\)

展开得到 \(\sum_{S}\sum_{T\subseteq S}(-1)^{|T|}\sum_k\prod_{i\in T\cup\overline{S}}q_i^k=\sum_{T\subseteq S}(-1)^T\dfrac{1}{1-\prod_{i\in{T\cup\overline S}}q_i}\)

求出 \(p_s=\dfrac{1}{1-\prod_{i\in s}q_i}\) 之后 FWT 可以得到每个 \(S\) 的贡献 \(f_S\)

然后我们要对每个可能的答案串 \(w\),计算所有 \(S\) 使得 \(S\cap w\) 唯一的 \(\sum f_S\),注意到这样的 \((w,S\cap w)\) 最多 \(\mathcal O(3^n)\) 对,因此 dfs 枚举 \(w\),对于每种 \(S\cap w\) 动态维护对应 \(w\) 是否唯一。

时间复杂度 \(\mathcal O(n2^n+3^n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1<<15|5,MOD=998244353;
void sub(int &x,const int &y) { x=(x>=y)?x-y:x+MOD-y; }
int ksm(int a,int b=MOD-2) { int s=1; for(;b;a=1ll*a*a%MOD,b>>=1) if(b&1) s=1ll*s*a%MOD; return s; }
int n,m,a[MAXN],b[16][MAXN],f[MAXN],g[MAXN];
void dfs(int s,int p,int e) {int *c=b[e];for(int t=s;;t=(t-1)&s) {if(c[t]>0) sub(g[c[t]],f[s]);if(!t) break;}if(!s) return ;int *d=b[e+1];for(int i=p;i<n;++i) if(s>>i&1) {for(int t=s;;t=(t-1)&s) {if(t>>i&1) d[t^(1<<i)]=c[t];else d[t]=(d[t]&&c[t])?-1:(d[t]|c[t]);if(!t) break;}dfs(s^(1<<i),i+1,e+1);}
}
void solve() {cin>>n>>m,memset(b,0,sizeof(b));for(int s=0;s<(1<<n);++s) f[s]=1;for(int i=0,w=ksm(10000),x;i<n;++i) {cin>>x,f[1<<i]=(1+1ll*(MOD-w)*x)%MOD;}for(int i=1;i<(1<<n);i<<=1) for(int s=0;s<(1<<n);++s) if(s&i) f[s]=1ll*f[s]*f[s^i]%MOD;for(int s=0;s<(1<<n);++s) f[s]=ksm(1+MOD-f[s]);for(int i=1;i<(1<<n);i<<=1) for(int s=0;s<(1<<n);++s) if(s&i) {swap(f[s],f[s^i]),sub(f[s],f[s^i]);}int w=0;for(int s=0;s<(1<<n);++s) w=(w+f[s])%MOD;for(int i=1;i<=m;++i) {string o; cin>>o,a[i]=0,g[i]=w;for(int j=0;j<n;++j) a[i]|=(o[j]-'0')<<j;b[0][a[i]]=i;}dfs((1<<n)-1,0,0);for(int i=1;i<=m;++i) cout<<g[i]<<"\n";
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int _; cin>>_;while(_--) solve();return 0;
}



O. [QOJ6103] A+B Problem (3)

Problem Link

题意就是构造 Halin 图的树分解,对每个点 \(u\) 维护子树中编号最小的叶子 \(l_u\),以及编号最大的叶子的后继 \(r_u\)

每个 \(u\) 建立节点 \((u,fa_u,l_u,r_u)\),和儿子 \(v\) 合并时建立 \((u,l_u,r_u,r_v)\) 中转。

可以证明点数 \(\le 2n\)

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

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n,m,fa[MAXN*4],l[MAXN],r[MAXN],id[MAXN],nx[MAXN];
vector <int> G[MAXN],S[MAXN*4];
void dfs(int u,int fz) {if(G[u].empty()) {id[u]=++m,S[m]={u,fz,nx[u]},l[u]=u,r[u]=nx[u];return ;}for(int v:G[u]) {dfs(v,u);if(!id[u]) { id[u]=id[v],l[u]=l[v],r[u]=r[v]; continue; }S[++m]={u,l[u],r[u],r[v]};fa[id[u]]=fa[id[v]]=m,r[u]=r[v],id[u]=m;}if(fz) S[++m]={u,fz,l[u],r[u]},fa[id[u]]=m,id[u]=m;
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n;for(int i=2,x;i<=n;++i) cin>>x,G[x].push_back(i);vector <int> h;for(int i=1;i<=n;++i) if(G[i].empty()) h.push_back(i);h.push_back(h[0]);for(int i=0;i+1<(int)h.size();++i) nx[h[i]]=h[i+1];dfs(1,0);cout<<m<<"\n";for(int i=1;i<=m;++i) {sort(S[i].begin(),S[i].end());S[i].erase(unique(S[i].begin(),S[i].end()),S[i].end());cout<<S[i].size();for(int x:S[i]) cout<<" "<<x;cout<<"\n";}for(int i=1;i<=m;++i) if(fa[i]) cout<<fa[i]<<" "<<i<<"\n";return 0;
}



*P. [QOJ8005] Crossing the Border (7)

Problem Link

直接对 \(2^n\) 种状态做子集 Exp 难以接受,考虑折半。

\(c\) 降序排序,把物品集合拆成前一半和后一半两部分,状态为 \(f_{s,t}\)

转移的集合也分成 \(x,y\) 两半,要求 \(w_x+w_y\le W\),这是经典的双指针形式。

注意到集合 \((x,y)\) 的信息只和 \(x\)\(\max c\) 有关,因此可以转移 \(f_{s,t-y}\to f_{s+x,t}\),在枚举 \(x\) 的同时维护合法的 \(y\) 对应 \(f_{s,t-y}\) 之和。

提前给每个 \(s,t\) 的所有子集排序即可,记得预处理 \(x=\varnothing\) 的集合贡献。

时间复杂度 \(\mathcal O(n3^{n/2}+2^{n/2}\times 3^{n/2})\)

代码:

#include<bits/stdc++.h>
#define lb __builtin_ctz
using namespace std;
const int inf=1.1e9+7,MOD=998244353;
struct info {int v,f;info(int V=inf,int F=0): v(V),f(F) {}inline friend info operator *(const info &u,const info &v) {return info(u.v+v.v,1ll*u.f*v.f%MOD);}inline friend info operator +(const info &u,const info &v) {if(u.v!=v.v) return u.v<v.v?u:v;return {u.v,(u.f+v.f)%MOD};}
}	f[1<<11][1<<11];
struct item { int w,c; } a[22];
int n,m,l,r,sl[1<<11],sr[1<<11];
basic_string <array<int,2>> br[1<<11];
array<int,2> bl[1<<11];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m,l=n/2,r=n-n/2;for(int i=0;i<n;++i) cin>>a[i].w>>a[i].c;sort(a,a+n,[&](auto x,auto y){ return x.c>y.c; });for(int i=0;i<(1<<l);++i) for(int j=0;j<l;++j) if(i>>j&1) sl[i]+=a[j].w;for(int i=0;i<(1<<r);++i) for(int j=0;j<r;++j) if(i>>j&1) sr[i]+=a[j+l].w;info *g=f[0]; g[0]={0,1};for(int s=1;s<(1<<r);++s) for(int t=s,x=lb(s);t;t=(t-1)&s) if((t>>x&1)&&sr[t]<=m) {g[s]=g[s]+g[s^t]*info(a[x+l].c,1);}for(int s=0;s<(1<<r);++s) {for(int t=s;;t=(t-1)&s) { br[s].push_back({sr[t],t}); if(!t) break; }sort(br[s].begin(),br[s].end(),greater<>());}for(int s=1;s<(1<<l);++s) {int q=0,d=lb(s);for(int t=s;t;t=(t-1)&s) if(t>>d&1) bl[q++]={sl[t],t};sort(bl,bl+q);for(int t=0;t<(1<<r);++t) {int y=0; info w;for(auto x:br[(1<<r)-1-t]) {for(;y<q&&x[0]+bl[y][0]<=m;++y) w=w+f[s-bl[y][1]][t]*info(a[d].c,1);f[s][t+x[1]]=f[s][t+x[1]]+w;}}}auto z=f[(1<<l)-1][(1<<r)-1];cout<<z.v<<" "<<z.f<<"\n";return 0;
}



Q. [QOJ7563] Fun on Tree (2)

Problem Link

\(f_u\) 表示子树内最小的 \(dep_x-a_x\)\(g_u\) 表示 \(T_u\setminus T_{hson(u)}\) 中最小的 \(dep_x-a_x-2dep_u\),修改就是子树加,然后更新轻祖先的 \(g\)

查询时维护链上的 \(\min g_u\) 就得到了答案。

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

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+5;
const ll inf=1e18;
typedef array<ll,2> pii;
int n,m;
struct Segt1 {pii tr[1<<19]; ll tg[1<<19];void adt(int p,ll k) { tr[p][0]+=k,tg[p]+=k; }void psu(int p) { tr[p]=max(tr[p<<1],tr[p<<1|1]); }void psd(int p) { if(tg[p]) adt(p<<1,tg[p]),adt(p<<1|1,tg[p]),tg[p]=0; }void add(int ul,int ur,ll k,int l=1,int r=n,int p=1) {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);}void upd(int u,pii z,int l=1,int r=n,int p=1) {if(l==r) return tr[p]=z,void();int mid=(l+r)>>1; psd(p);u<=mid?upd(u,z,l,mid,p<<1):upd(u,z,mid+1,r,p<<1|1);psu(p);}pii qry(int ul,int ur,int l=1,int r=n,int p=1) {if(ul>ur) return {-inf,-inf};if(ul<=l&&r<=ur) return tr[p];int mid=(l+r)>>1; pii s={-inf,-inf}; psd(p);if(ul<=mid) s=max(s,qry(ul,ur,l,mid,p<<1));if(mid<ur) s=max(s,qry(ul,ur,mid+1,r,p<<1|1));return s;}
}	T,Q;
struct Edge { int v,w; };
vector <Edge> G[MAXN];
ll a[MAXN],d[MAXN];
int siz[MAXN],hson[MAXN],fa[MAXN],top[MAXN],dfn[MAXN],dcnt,efn[MAXN];
void dfs1(int u,int fz) {fa[u]=fz,siz[u]=1;for(auto e:G[u]) {d[e.v]=d[u]+e.w,dfs1(e.v,u),siz[u]+=siz[e.v];if(siz[e.v]>siz[hson[u]]) hson[u]=e.v;}
}
void upd(int u) {pii x=T.qry(dfn[u],dfn[u]);if(hson[u]) x=max(x,T.qry(efn[hson[u]]+1,efn[u]));Q.upd(dfn[u],{x[0]-2*d[u],x[1]});
}
void dfs2(int u,int h) {top[u]=h,dfn[u]=++dcnt,T.upd(dfn[u],{d[u]-a[u],-u});if(hson[u]) dfs2(hson[u],h);for(auto e:G[u]) if(e.v!=hson[u]) dfs2(e.v,e.v);efn[u]=dcnt,upd(u);
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=n;++i) cin>>a[i];for(int i=2,x,y;i<=n;++i) cin>>x>>y,G[x].push_back({i,y});dfs1(1,0),dfs2(1,1);for(int x,y,z;m--;) {cin>>x>>y>>z;T.add(dfn[y],efn[y],-z),Q.add(dfn[y],efn[y],-z);for(int u=y;u;u=fa[top[u]]) upd(u);pii s=T.qry(dfn[x],efn[x]); s[0]-=2*d[x];for(int u=x;u;u=fa[u]) {s=max(s,Q.qry(dfn[top[u]],dfn[u]-1)),u=top[u];if(u>1) {pii o=max(T.qry(dfn[fa[u]],dfn[u]-1),T.qry(efn[u]+1,efn[fa[u]]));s=max(s,{o[0]-2*d[fa[u]],o[1]});}}cout<<-s[1]<<" "<<s[0]+d[x]<<"\n";}return 0;
}



R. [QOJ10174] Lost Table (2)

Problem Link

容斥相当于选一些 \(a_i,b_i\) 减一,然后维护 \(\prod_{i,j}\min(a_i,b_j)\),从小到大枚举每个值 \(v\),容易算出有多少 \(\min(a_i,b_j)=v\)

考虑给一些 \(a_i,b_i\) 减一不影响 \(v\) 之间的大小关系,对每个 \(v\) 内部选一些 \(a_i,b_j\) 减一,然后先算 \(v-1\) 的贡献再算 \(v\) 的贡献。

简单处理可以把 \(b_j\) 个数的枚举用二项式定理优化掉。

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

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+5,MOD=1e9+7;
ll fac[MAXN],ifac[MAXN];
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 C(int x,int y) { return y<0||y>x?0:fac[x]*ifac[y]%MOD*ifac[x-y]%MOD; }
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);for(int i=fac[0]=ifac[0]=1;i<MAXN;++i) ifac[i]=ksm(fac[i]=fac[i-1]*i%MOD);int n,m; cin>>n>>m;map <int,array<int,2>> a;for(int i=1,x;i<=n;++i) cin>>x,++a[x][0];for(int i=1,x;i<=m;++i) cin>>x,++a[x][1];ll z=1,s=n,t=m;for(auto it:a) {int v=it.first,x=it.second[0],y=it.second[1];ll w=0;for(int i=0;i<=x;++i) {ll b=(MOD-ksm(v-1,s-i))*ksm(v,MOD-1+i-s)%MOD;w=(w+(i&1?-1:1)*ksm(b+1,y)*C(x,i)%MOD*ksm(v-1,i*t)%MOD*ksm(v,(x-i)*t+y*(s-x)))%MOD;}z=z*(w+MOD)%MOD,s-=x,t-=y;}cout<<z<<"\n";return 0;
}



*S. [QOJ6653] 阴阳阵法 (7)

Problem Link

考虑 \(f_{x,y}\) 表示 \(x\) 个白点 \(y\) 个黑点的方案数,初始 \(f_{x,0}=(n+m)^x\),初始令 \(f_{x,y}=f_{x,y-1}\times n\),然后容斥掉加入最后一个黑点时产生环的方案数。

具体来说枚举环上黑白点个数 \(i,j\),然后组合数计算方案,复杂度 \(\mathcal O(n^4)\)

考虑优化,设 \(g_{x,y,0/1,0/1,0/1}\) 表示容斥过程中(算上环外节点)有 \(x\) 个白点 \(y\) 个黑点,两种点奇偶性为 \(0/1\),当前环为颜色为黑或白的方案数,每次加入一个环末节点即可。

容斥就是 \(f_{x,y}\) 减掉 \(g_{x,y-1,1,0,0}\),即插入最后一个黑点后成环的情况,注意我们要钦定 \(g\) 中计算的环首是白点。

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

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2005;
int MOD,n,m,f[MAXN][MAXN],g[MAXN][MAXN][2][2][2];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m>>MOD;for(int i=0;i<=n;++i) {f[i][0]=i?1ll*f[i-1][0]*(n+m)%MOD:1;for(int j=1;j<=m;++j) {f[i][j]=(1ll*n*f[i][j-1]+MOD-g[i][j-1][1][0][0])%MOD;}for(int j=0;j<=m;++j) { g[i+1][j][1][0][0]=(g[i+1][j][1][0][0]+1ll*(i+1)*f[i][j])%MOD;for(int x:{0,1}) for(int y:{0,1}) {g[i+1][j][x^1][y][0]=(g[i+1][j][x^1][y][0]+1ll*(i+1)*(g[i][j][x][y][0]+g[i][j][x][y][1]))%MOD;g[i][j+1][x][y^1][1]=(g[i][j+1][x][y^1][1]+1ll*(j+1)*g[i][j][x][y][0])%MOD;}}}cout<<f[n][m]<<"\n";return 0;
}



T. [QOJ6101] Ring Road (3.5)

Problem Link

Halin 图上最短路,建立树分解后点分治,对重心上的每个节点跑一遍 Dijkstra。

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

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+5;
int n,m,q,l[MAXN],r[MAXN],id[MAXN],nx[MAXN];
struct Edge { int v; ll w; };
vector <Edge> G[MAXN];
vector <int> S[MAXN],E[MAXN];
void link(int x,int y) { E[x].push_back(y),E[y].push_back(x); }
void dfs1(int u,int fz) {if(G[u].size()==1&&fz) {id[u]=++m,S[m]={u,fz,nx[u]},l[u]=u,r[u]=nx[u];return ;}for(auto e:G[u]) if(e.v^fz) {int v=e.v; dfs1(v,u);if(!id[u]) { id[u]=id[v],l[u]=l[v],r[u]=r[v]; continue; }S[++m]={u,l[u],r[u],r[v]};link(m,id[u]),link(m,id[v]),r[u]=r[v],id[u]=m;}if(fz) S[++m]={u,fz,l[u],r[u]},link(m,id[u]),id[u]=m;
}
bool vis[MAXN],inq[MAXN],tmp[MAXN];
int fa[MAXN],b[MAXN],siz[MAXN],cur[MAXN],st[MAXN],tp,ed[MAXN];
ll dis[100][MAXN];
void solve(int u) {function<void(int,int)> dfs4=[&](int x,int fz) {siz[x]=1;for(int o:S[x]) if(!tmp[o]&&!inq[o]) tmp[o]=true,st[++tp]=o;for(int y:E[x]) if(!vis[y]&&y!=fz) dfs4(y,x),siz[x]+=siz[y];};dfs4(u,0); int o=b[u]*4;for(int s:S[u]) if(tmp[s]) {ll *d=dis[o++];priority_queue<array<ll,2>,vector<array<ll,2>>,greater<>> Q;Q.push({d[s]=0,s});while(Q.size()) {ll z=Q.top()[0],x=Q.top()[1]; Q.pop();if(z!=d[x]) continue;vector <Edge> h;for(auto e:G[x]) if(tmp[e.v]) {h.push_back(e);if(d[e.v]>d[x]+e.w) Q.push({d[e.v]=d[x]+e.w,e.v});}G[x].swap(h);}tmp[s]=false,inq[s]=true,ed[s]=u;}while(tp) tmp[st[tp--]]=0;
}
void dfs2(int u) {solve(u),vis[u]=true;for(int v:E[u]) if(!vis[v]) {int rt=0;function<void(int,int)> dfs3=[&](int x,int fz) {cur[x]=siz[v]-siz[x];for(int y:E[x]) if(y!=fz&&!vis[y]) dfs3(y,x),cur[x]=max(cur[x],siz[y]);if(!rt||cur[rt]>cur[x]) rt=x;};dfs3(v,u),b[rt]=b[u]+1,fa[rt]=u,dfs2(rt);}
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n; ll z;for(int i=2,x;i<=n;++i) cin>>x>>z,G[x].push_back({i,z}),G[i].push_back({x,z});vector <int> h;for(int i=2;i<=n;++i) if(G[i].size()==1) h.push_back(i);h.push_back(h[0]);for(int i=0;i+1<(int)h.size();++i) nx[h[i]]=h[i+1];dfs1(1,0);for(int i=0;i+1<(int)h.size();++i) cin>>z,G[h[i]].push_back({h[i+1],z}),G[h[i+1]].push_back({h[i],z});for(int i=1;i<=m;++i) {sort(S[i].begin(),S[i].end());S[i].erase(unique(S[i].begin(),S[i].end()),S[i].end());}memset(dis,0x3f,sizeof(dis)),dfs2(1);cin>>q;for(int u,v,x,y;q--;) {cin>>u>>v,z=1e18;for(x=ed[u],y=ed[v];x^y;b[x]<b[y]?y=fa[y]:x=fa[x]);for(int i=b[x]*4+3;~i;--i) z=min(z,dis[i][u]+dis[i][v]);cout<<z<<"\n";}return 0;
}



U. [QOJ9699] Loving You in My Humble Way (6.5)

Problem Link

正宗 Ad-Hoc。

直接把一条边看成 \((x,y),(y,z),(z,x)\) 三条边,限制变成没有四元环。

考虑空间中的直线,如果两条直线垂直就连边,可以证明图上没有四元环。

那么取质数 \(p\),在 \(\bmod p\) 意义下取出所有不平行直线,垂直就连边,可以证明不存在四元环,可以通过把其中两条直线变成 \((1,0,0),(0,1,0)\) 证明。

此时点数 \(p^2+p+1\),取 \(p=43\),直接数图中的三元环个数发现恰好合法。

时间复杂度 \(\mathcal O\left(\dfrac{n^3}\omega\right)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int n=43,M=1893;
array<int,3> a[M];
bitset <M> f[M],g;
signed main() {int m=0; a[m++]={0,0,1};for(int i=0;i<n;++i) a[m++]={0,1,i};for(int i=0;i<n;++i) for(int j=0;j<n;++j) a[m++]={1,i,j};cout<<n*(n-1)*(n+1)/6<<"\n";for(int i=0;i<m;++i) for(int j=i+1;j<m;++j) if((a[i][0]*a[j][0]+a[i][1]*a[j][1]+a[i][2]*a[j][2])%n==0) f[i].set(j);for(int i=0;i<m;++i) for(int j=i+1;j<m;++j) if(f[i][j]) {g=f[i],g&=f[j];for(int k=g._Find_first();k<M;k=g._Find_next(k)) cout<<i+1<<" "<<j+1<<" "<<k+1<<"\n";}return 0;
}



V. [QOJ10167] Random Interactive MST Bot (4.5)

Problem Link

考虑稠密图上表现最优秀的 Prim 算法,注意到我们只要维护每个 \(u\) 到当前点集的最小值 \(d_u\),那么取出每次更新 \(d_u\) 的边权,\(d_u\) 的实际变化次数就是其中前缀最小值的期望个数,显然是 \(\mathcal O(\log n)\) 级别的。

用 zkw 线段树维护所有 \(d_u\),增加时自下而上更新区间 \(\min d_u\),如果更新不了可以直接返回。

加上朴素记忆化即可通过,交互次数在 \(5900\) 次左右。

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

代码:

#include<bits/stdc++.h> 
using namespace std;
typedef array<int,2> pii;
const int N=128,M=5005;
int L(pii x) { return min(x[0],x[1]); }
int R(pii x) { return max(x[0],x[1]); }
int n,m,id[N][N];
bitset <M> g[M],w[M];
bool ask(pii x,pii y) {if(!x[0]||!y[0]) return x[0];int u=id[L(x)][R(x)],v=id[L(y)][R(y)];if(w[u][v]) return g[u][v];cout<<"? "<<L(x)<<" "<<R(x)<<" "<<L(y)<<" "<<R(y)<<endl;int o; cin>>o;w[u][v]=w[v][u]=1,g[u][v]=o,g[v][u]=o^1;return o;
}
bool vis[N];
pii d[N],f[N<<1];
void upd(int x,pii z) {for(x+=N;x&&ask(z,f[x]);x>>=1) f[x]=z;
}
void del(int x) {for(f[x+=N]={0,0};x>1;x>>=1) f[x>>1]=(ask(f[x],f[x^1])?f[x]:f[x^1]);
}
signed main() {cin>>n;for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) id[i][j]=++m;vis[1]=true;for(int i=2;i<=n;++i) upd(i,d[i]={1,i});vector <pii> z;for(int t=1;t<n;++t) {int u=f[1][1];z.push_back({f[1][0],u});vis[u]=true,del(u);for(int i=1;i<=n;++i) if(!vis[i]) upd(i,{u,i});}cout<<"!"; for(auto t:z) cout<<" "<<L(t)<<" "<<R(t); cout<<endl;return 0;
}



*W. [QOJ8012] Jumping Lights (8)

Problem Link

考虑同时维护当前树以及进行二操作后的树。

对于一个翻转操作我们将该点记录下来,在进行二操作前更新这些点的贡献。

具体来说对每个点分成父亲、叶子儿子、非叶子儿子,父亲节点可以在翻转时记录,叶子儿子直接对每个点记录个数并维护覆盖标记,非叶子儿子用哈希表维护未标记的部分并暴力更新。

可以通过势能分析证明复杂度是线性的。

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

代码:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
const int MAXN=3e5+5;
vector <int> G[MAXN];
int n,q,fa[MAXN],nc[MAXN],lc[MAXN];
struct ds {bool a[MAXN],b[MAXN];basic_string <int> Q;gp_hash_table <int,int> E[MAXN];int sc[MAXN],vs[MAXN],vc,z;bool qcol(int u) { return vs[fa[u]]>vs[u]?b[fa[u]]:a[u]; }bool ncol(int u) { return a[fa[u]]||sc[u]||(int)E[u].size()<nc[u]; }void updL(int u,int c) {//leafvs[u]=++vc,a[u]=c,z+=2*c-1,sc[fa[u]]+=2*c-1;}void updV(int u,int c) { //no leafa[u]=c,z+=2*c-1;if(fa[u]) {if(c) E[fa[u]].erase(u);else E[fa[u]].insert({u,0});}}void updN(int u,int c) { //neighbourvs[u]=++vc,b[u]=c,z-=sc[u],sc[u]=c*lc[u],z+=sc[u];}
}	T[2];
void updL(int o,int u,int c) {if(T[o].qcol(u)==c) return ;T[0].Q+=fa[u],T[1].Q+=fa[u],T[o].updL(u,c);
}
void updV(int o,int u,int c) {if(T[o].a[u]==c) return ;T[0].Q+=u,T[1].Q+=u;if(fa[u]) T[o].Q+=fa[u];T[o].updV(u,c);
}
int vis[MAXN],vc=0;
void solve(int o) {basic_string<int>Q; Q.swap(T[o].Q),++vc;for(int u:Q) if(vis[u]<vc) {int x=T[o].a[u],y=T[o].ncol(u);updV(o^1,u,y),T[o^1].updN(u,x),vis[u]=vc;if(x) {gp_hash_table<int,int>E; E.swap(T[o^1].E[u]);if(fa[u]) updV(o^1,fa[u],1);for(auto i:E) updV(o^1,i.first,1);}}
}
bool il[MAXN];
void dfs(int u,int fz) {il[u]=(fz&&G[u].size()==1),fa[u]=fz;for(int v:G[u]) if(v^fz) {dfs(v,u),lc[u]+=il[v],nc[u]+=!il[v];if(!il[v]) for(int o:{0,1}) T[o].E[u].insert({v,0});}
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>q;for(int i=1,u,v;i<n;++i) cin>>u>>v,G[u].push_back(v),G[v].push_back(u);dfs(1,0);for(int o=0,c,u;q--;) {cin>>c;if(c==2) solve(o),o^=1;else cin>>u,(il[u]?updL(o,u,c):updV(o,u,c));cout<<T[o].z<<" \n"[!q];}return 0;
}



*X. [QOJ6651] 喵了个喵 III (9)

Problem Link

考虑最后一对删除的元素 \(x\),那么序列被分成 \(AxBxC\)

那么操作过程一定形如 \([A][x]\to[][x,B]\to [x,C][x,B]\to [x][x]\),注意到 \(A\) 中未消除的元素必须在同一个栈中,因此 \(A\) 中剩余的元素只有恰好出现过一次的元素,并且按顺序排列之。

考虑 \(A,B,C\) 三部分过程分开,可以定义状态 \(f_{l,r}\) 表示 \(a[1,l-1]\) 中出现过一次的元素放在其中一个栈,另一个栈为空,能否操作 \(a[l,r]\) 使得最终 \(a[1,r]\) 中出现过一次的元素放入另一个栈。

考虑 \(f\) 的转移,我们还需要 \(g_{l,r}\) 表示最终把 \(a[1,r]\) 中出现过一次的元素放入同一个栈。

以及 \(h_{l,r}\) 表示 \([1,r]\) 内部元素两两匹配时能否完全消除。

分讨一下转移:

  • \(f\gets g+f\):记 \(x\)\([l,r]\) 中第一个匹配 \([r+1,n]\) 的元素,序列为 \(A+BxC\),那么过程为 \([A][]\to [A,B][x]\to [][x,C]\)

注意这里 \(g\) 操作要求 \(AB\) 中能匹配的点是 \(A\) 的一段栈顶,因此不能有 \(C\to i,B\to j\) 满足 \(j<i,i,j\in A\),否则 \(j\) 消不掉。

  • \(g\gets f+f\):同上定义 \(x,A,B,C\),过程为 \([A][]\to [A,x][B]\to [A,x,C][]\)

    这里要求 \(A,C\) 无边。

  • \(h\gets f+f+h\):枚举 \([l,r]\) 最后一对消除元素 \(x\),设序列为 \(A+BxCxD\),那么过程为:\([A][]\to [A,x][B]\to [A,x,C][B,x]\to [A,x][B,x]\)

    要求 \((A,C),(A,D),(B,D)\) 无边。

  • \(h\gets g+f+h\):同上定义 \(x,A,B,C,D\),过程为:\([A][]\to [A,B][x]\to [A,B,x][x,C]\to [A,B,x][x]\)

    要求 \((A,D),(B,D)\) 无边,并且类似情况一,\(C,D\) 中元素不占用 \(A\) 的栈顶。

  • \(h\gets g+h\):这种情况下依然枚举 \(x\),但与 \(x\) 匹配的元素 \(\in a[1,l-1]\),设序列为 \(A+BxC\),过程为:\([A][]\to [AB][x]\to [AB][xC]\to [A'x][x]\)

    这种情况下依然要求 \(C\) 不占用 \(A\) 的栈顶,且任何非 \(x\) 元素不能 \(\in A'\),直接取 \([l,r]\)\(p\) 的最小值为 \(x\)

注意到 \(h_{l,r}\) 本质是 \([l,r]\) 中没有向 \(>r\) 匹配元素时 \(f_{l,r}=g_{l,r}=h_{l,r}\),按照上述过程预处理判定条件然后暴力 dp,记录决策点然后模拟上述策略构造。

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

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1505;
int n,a[MAXN],t[MAXN],p[MAXN],ls[MAXN][MAXN],rp[MAXN][MAXN],mn[MAXN][MAXN],mx[MAXN][MAXN],cv[MAXN][MAXN];
int h[MAXN][MAXN],b[MAXN];
bool f[MAXN][MAXN],g[MAXN][MAXN];
void dp(int l,int r,bool o,bool c) {if(l>r) return ;if(ls[l][r]<=r) {int i=ls[l][r];if(o) b[i]=c,dp(l,i-1,0,c),dp(i+1,r,0,c^1);else b[i]=c^1,dp(l,i-1,1,c),dp(i+1,r,0,c);return ;}int i=h[l][r];if(i<=n) {dp(l,i-1,0,c),b[i]=c,dp(i+1,p[i]-1,0,c^1),b[p[i]]=c^1,dp(p[i]+1,r,0,c);} else if(i-n<=n) {i-=n,dp(l,i-1,1,c),b[i]=c^1,dp(i+1,p[i]-1,0,c),b[p[i]]=c,dp(p[i]+1,r,0,c^1);} else {i-=2*n,dp(l,i-1,1,c),b[i]=c^1,dp(i+1,r,0,c);}
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;++i) {cin>>a[i];if(t[a[i]]) p[i]=t[a[i]],p[p[i]]=i;else t[a[i]]=i;}for(int i=0;i<=n;++i) f[i+1][i]=g[i+1][i]=true;memset(ls,0x3f,sizeof(ls));memset(mn,0x3f,sizeof(mn));memset(cv,0x3f,sizeof(cv));for(int l=1;l<=n;++l) {for(int r=l;r<=n;++r) {rp[l][r]=p[r]<l?r:rp[l][r-1];mn[l][r]=min(mn[l][r-1],p[r]);mx[l][r]=max(mx[l][r-1],p[r]);}memset(t,0x3f,sizeof(t));for(int r=n;r>=l;--r) {for(int x=mn[l][r];x<=n;x+=x&-x) cv[l][r]=min(cv[l][r],t[x]);if(p[r]<l) for(int x=p[r];x;x&=x-1) t[x]=r;}}for(int r=1;r<=n;++r) for(int l=r;l>=1;--l) {ls[l][r]=p[l]>r?l:ls[l+1][r];}for(int l=n;l;--l) for(int r=l;r<=n;++r) {if(ls[l][r]<=r) {int i=ls[l][r];if(rp[l][r]<i&&f[l][i-1]&&f[i+1][r]) g[l][r]=true;if(r<cv[l][i-1]&&g[l][i-1]&&f[i+1][r]) f[l][r]=true;continue;}for(int i=l;i<=r;++i) if(i<p[i]) {if(rp[l][r]<i&&mx[l][i-1]<=p[i]&&f[l][i-1]&&f[i+1][p[i]-1]&&f[p[i]+1][r]) f[l][r]=true,h[l][r]=i;if(mn[p[i]+1][r]>=i&&cv[l][i-1]>r&&g[l][i-1]&&f[i+1][p[i]-1]&&f[p[i]+1][r]) f[l][r]=true,h[l][r]=n+i;if(f[l][r]) goto sc;}if(mn[l][r]<l) {int i=p[mn[l][r]];if(cv[l][i-1]>r&&g[l][i-1]&&f[i+1][r]) f[l][r]=true,h[l][r]=2*n+i;}sc:g[l][r]=f[l][r];}if(!f[1][n]) return cout<<"No solution.\n",0;dp(1,n,0,0);cout<<"Cleared.\n"<<n/2*3<<"\n";vector <int> c[2];for(int i=1;i<=n;++i) {cout<<b[i]+1,c[b[i]].push_back(a[i]);for(;c[0].size()&&c[1].size()&&c[0].back()==c[1].back();c[0].pop_back(),c[1].pop_back()) cout<<0;}cout<<"\n";return 0;
}



Y. [QOJ7994] 勿蹖宠物 (3)

Problem Link

考虑从中间开始向两侧填字符串来维护回文串,每次转移长度较小的一侧,只需 dp 记录长度较长一侧剩余的字符串是哪个前缀或者哪个后缀。

预处理每个状态加上一个字符串后的转移即可。

时间复杂度 \(\mathcal O(nS^2+nmS)\),其中 \(S=\sum|s_i|\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
int n,m,q,d[355],f[1005][1205],L[335][605],R[335][605],g[1205][355];
string s[355];
void add(int &x,const int &y) { x=(x+y>=MOD)?x+y-MOD:x+y; }
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=n;++i) {cin>>s[i],d[i]=s[i].size();for(int j=0;j<d[i];++j) L[i][j]=++q,R[i][j]=++q;}for(int i=1;i<=n;++i) g[0][i]=R[i][0];for(int i=1;i<=n;++i) for(int j=0;j<d[i];++j) {for(int k=1;k<=n;++k) {int &z=g[L[i][j]][k];for(int x=0;x<min(j+1,d[k]);++x) if(s[i][j-x]!=s[k][x]) { z=-1; break; }if(z<0) continue;if(j+1<d[k]) z=R[k][j+1];else if(j+1>d[k]) z=L[i][j-d[k]];else z=0;}for(int k=1;k<=n;++k) {int &z=g[R[i][j]][k];for(int x=0;x<min(d[i]-j,d[k]);++x) if(s[i][j+x]!=s[k][d[k]-1-x]) { z=-1; break; }if(z<0) continue;if(d[i]-j<d[k]) z=L[k][d[k]-(d[i]-j)-1];else if(d[i]-j>d[k]) z=R[i][j+d[k]];else z=0;}}if(m%2==0) {++f[m][0];for(int i=1;i<=n;++i) if(d[i]<=m) for(int j=0;j+1<d[i];++j) {for(int x=0;x<min(j+1,d[i]-1-j);++x) if(s[i][j-x]!=s[i][j+1+x]) goto n1;if(j+1<d[i]-1-j) ++f[m-d[i]][R[i][2*j+2]];else if(j+1>d[i]-1-j) ++f[m-d[i]][L[i][2*j-d[i]+1]];else ++f[m-d[i]][0];n1:;}} else {for(int i=1;i<=n;++i) if(d[i]<=m) for(int j=0;j<d[i];++j) {for(int x=0;x<min(j,d[i]-1-j);++x) if(s[i][j-1-x]!=s[i][j+1+x]) goto n2;if(j<d[i]-1-j) ++f[m-d[i]][R[i][2*j+1]];else if(j>d[i]-1-j) ++f[m-d[i]][L[i][2*j-d[i]]];else ++f[m-d[i]][0];n2:;}}for(int i=m;i;--i) for(int j=0;j<=q;++j) if(f[i][j]) {for(int k=1;k<=n;++k) if(i>=d[k]&&~g[j][k]) add(f[i-d[k]][g[j][k]],f[i][j]);}cout<<f[0][0]<<"\n";return 0;
}



Z. [QOJ6650] Freshman Dream (4)

Problem Link

解方程 \(\sum a_{i,k}b_{k,j}=a_{i,j}b_{i,j}\),容易发现每组 \(j\) 相同的 \(b\) 构成 \(n\)\(n\) 次方程组。

高斯消元之后暴力枚举每个自由元的取值,然后背包。

由于 \(a\) 随机,因此矩阵秩 \(\le n-k\) 的概率为 \(\mathcal O\left(\dfrac 1{k!}\right)\) 级别。

时间复杂度 \(\mathcal O\left(\dfrac{n^4}\omega\right)\)

代码:

#include<bits/stdc++.h>
using namespace std;
bitset <105> a[105],f[105],o;
vector <bitset<105>> g[105],h;
int dp[105][10005];
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int n,k; cin>>n>>k;for(int i=1,c;i<=n;++i) for(int j=1;j<=n;++j) cin>>c,a[i][j]=c;memset(dp,-1,sizeof(dp)),dp[0][0]=0;for(int t=1;t<=n;++t) {for(int i=1;i<=n;++i) f[i].reset();for(int i=1;i<=n;++i) {o=a[i],o[i]=o[i]^a[i][t];for(int j=1;j<=n;++j) if(o[j]) {if(!f[j][j]) { f[j]=o; break; }else o^=f[j];}}for(int i=n;i;--i) if(f[i].any()) {for(int j=i-1;j;--j) if(f[j][i]) f[j]^=f[i];}h.clear();for(int i=n;i;--i) if(f[i].none()) {for(int j=1;j<=n;++j) o[j]=f[j][i];o[i]=1,h.push_back(o);}for(int s=0,c=h.size();s<(1<<c);++s) {o.reset();for(int x=0;x<c;++x) if(s>>x&1) o^=h[x];g[t].push_back(o); int z=o.count();for(int i=z;i<=k;++i) if(~dp[t-1][i-z]) dp[t][i]=s;}}if(dp[n][k]<0) return cout<<"-1\n",0;for(int i=1;i<=n;++i) a[i].reset();for(int t=n;t;--t) {int s=dp[t][k];for(int i=1;i<=n;++i) a[i][t]=g[t][s][i];k-=g[t][s].count();}cout<<"1\n";for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) cout<<a[i][j]<<" \n"[j==n];return 0;
}



AA. [QOJ7718] Coconuts (2)

Problem Link

\(f_{a}\) 表示攻击次数为 \([a_1,a_2,\dots,a_n]\) 时的答案,只用关心 \(a_i\) 的无序集,转移的时候计算一下可能的 \(d\) 序列个数即可。

时间复杂度 \(\mathcal O(\mathrm{Bell}(n)\mathrm{poly}(n))\)

代码:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#define ll long long
using namespace std;
int n,a[12],b[12],c[12],d[12],e[12];
int qry() {memset(d,0,sizeof(d)),memset(e,0,sizeof(e));for(int i=0;i<n;++i) ++e[a[i]],d[b[i]]+=!c[i];int s=1;for(int i=0;i<n;++i) if(c[i]) s*=e[b[i]]--;if(!s) return 0;for(int i=0,k=0;i<10;++i) for(k+=d[i];e[i+1]--;s*=k--);return s;
}
__gnu_pbds::gp_hash_table <ll,int> F;
int dfs(int q) {int o=qry();if(!o||!q) return count(c,c+n,1)*o;for(int i=0;i<n;++i) e[i]=b[i]<<1|c[i];sort(e,e+n); ll h=0;for(int i=0;i<n;++i) h=h*22+e[i];if(F.find(h)!=F.end()) return F[h];int z=0;for(int i=0;i<n;++i) if(!c[i]) {++b[i]; int t=dfs(q-1);c[i]=1,t+=dfs(q-1),c[i]=0,--b[i],z=max(z,t);}return F[h]=z;
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int k; cin>>n>>k;for(int i=0;i<n;++i) cin>>a[i];cout<<fixed<<setprecision(20)<<1.*dfs(k)/qry()<<"\n";return 0;
}



AB. [QOJ8007] Egg Drop Challenge (4.5)

Problem Link

考虑 \(i\to j\) 的转移,设速度为 \(v\),则 \(v\le u_j,v^2\le v_j-2(h_i-h_j)\),分讨 \(v\) 的具体取值,可以写成 \(f_i\gets z+\sqrt{x_i-k}\)\(f_i\gets z-\sqrt{k-x_i}\)

注意到这样的函数之间只有一个交点,因此可以用类似李超线段树的方式维护最小值,\(v\) 取值的限制可以 CDQ 分治去掉,注意 \(x\) 超出定义域的部分不能插入该函数。

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

#include<bits/stdc++.h>
#define ll long long
#define ld long double
using namespace std;
const int MAXN=3e5+5;
const ll inf=4e18;
struct info {ld z; ll k; int o;inline ld f(ll x) const {if(o<0) return inf;if(!o) return x>k?inf:z+sqrtl(k-x);else return k>x?inf:z-sqrtl(x-k);}
}	e[MAXN];
struct Segt {int ls[MAXN*20],rs[MAXN*20],tr[MAXN*20],tot,rt;vector <ll> Z;void ins(int x,int ul,int ur,int l,int r,int &p) {if(ul<=l&&r<=ur) {if(!p) return tr[p=++tot]=x,void();int mid=(l+r)>>1;if(e[x].f(Z[mid])<e[tr[p]].f(Z[mid]))swap(tr[p],x);if(l==r) return ;if(e[x].f(Z[l])<e[tr[p]].f(Z[l])) ins(x,ul,ur,l,mid,ls[p]);if(e[x].f(Z[r])<e[tr[p]].f(Z[r])) ins(x,ul,ur,mid+1,r,rs[p]);return ;}if(!p) p=++tot;int mid=(l+r)>>1;if(ul<=mid) ins(x,ul,ur,l,mid,ls[p]);if(mid<ur) ins(x,ul,ur,mid+1,r,rs[p]);}ld ask(int x,int l,int r,int p) {ld z=e[tr[p]].f(Z[x]);if(!p||l==r) return z;int mid=(l+r)>>1;return min(z,x<=mid?ask(x,l,mid,ls[p]):ask(x,mid+1,r,rs[p]));}void init(vector<ll>&z) {for(int i=1;i<=tot;++i) ls[i]=rs[i]=tr[i]=0;tot=rt=0,Z.swap(z),sort(Z.begin(),Z.end()),Z.erase(unique(Z.begin(),Z.end()),Z.end());}void add(int x) {if(!e[x].o) {int t=upper_bound(Z.begin(),Z.end(),e[x].k)-Z.begin()-1;if(t>=0) ins(x,0,t,0,Z.size()-1,rt);} else {int t=lower_bound(Z.begin(),Z.end(),e[x].k)-Z.begin();if(t<(int)Z.size()) ins(x,t,Z.size()-1,0,Z.size()-1,rt);}}ld qry(ll x) {return ask(lower_bound(Z.begin(),Z.end(),x)-Z.begin(),0,Z.size()-1,rt);}
}	T;
int n,rt;
ll h[MAXN],L[MAXN],R[MAXN];
ld f[MAXN];
array <ll,2> a[MAXN];
void cdq(int l,int r) {if(l==r) return void();int mid=(l+r)>>1;cdq(mid+1,r);vector <ll> z1,z2;for(int j=l;j<=mid;++j) a[j]={L[j]*L[j]+2*h[j],j},z1.push_back(2*h[j]),z2.push_back(a[j][0]);for(int i=mid+1;i<=r;++i) a[i]={R[i]*R[i]+2*h[i],i};sort(a+l,a+mid+1),sort(a+mid+1,a+r+1);T.init(z1),rt=0;for(int j=l,k=mid+1,i;j<=mid;++j) {for(;k<=r&&a[k][0]<=a[j][0];++k) i=a[k][1],e[i]={f[i]-R[i],a[k][0],0},T.add(i);i=a[j][1],f[i]=min(f[i],T.qry(2*h[i]));}T.init(z2),rt=0;for(int j=mid,k=r,i;j>=l;--j) {for(;k>mid&&a[k][0]>a[j][0];--k) i=a[k][1],e[i]={f[i],2*h[i],1},T.add(i);i=a[j][1],f[i]=min(f[i],T.qry(a[j][0])+L[i]);}cdq(l,mid);
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n,e[0]={0,0,-1};for(int i=1;i<=n;++i) cin>>h[i]>>R[i]>>L[i];fill(f+1,f+n,inf),cdq(1,n);if(f[1]>=inf) cout<<"-1\n";else cout<<fixed<<setprecision(20)<<f[1]<<"\n";return 0;
}



AC. [QOJ6656] 先人类的人类选别 (4.5)

Problem Link

考虑一个前缀操作后的变化,如果 \(x\) 小于其中最小值那么无事发生,否则前缀最小值会被弹出。

因此一个前缀 \(a[1,k]\) 操作若干次后剩余的元素就是所有 \(x\) 加上原有元素的前 \(k\) 大。

主席树维护当前所有的 \(x\) 以及每个 \(a[1,k]\),简单二分即可。

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

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=5e5+5;
struct Segt {ll su[MAXN*40];int ct[MAXN*40],ls[MAXN*40],rs[MAXN*40],tot;void ins(int x,int l,int r,int q,int &p) {ct[p=++tot]=ct[q]+1,su[p]=su[q]+x;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 qry(int k,int l,int r,int q,int p) {if(l==r) return 1ll*k*l;int mid=(l+r)>>1,w=ct[rs[q]]+ct[rs[p]];if(k<=w) return qry(k,mid+1,r,rs[q],rs[p]);return qry(k-w,l,mid,ls[q],ls[p])+su[rs[q]]+su[rs[p]];}
}	T;
int n,m,rt[MAXN],o;
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1,x;i<=n;++i) cin>>x,T.ins(x,1,n,rt[i-1],rt[i]);for(int x,l,r;m--;) {cin>>x>>l>>r,T.ins(x,1,n,o,o);cout<<T.qry(r,1,n,rt[r],o)-T.qry(l-1,1,n,rt[l-1],o)<<"\n";}return 0;
}



AD. [QOJ9691] Little, Cyan, Fish! (3.5)

Problem Link

先把操作线段处理成互不相交的,然后按行扫描线,维护每个元素在三种操作中是否被覆盖容易用 bitset 优化,然后我们要统计每 \(\omega\) 位的贡献,拆成 \(4\) 个大小为 \(\dfrac\omega 4\) 的部分预处理即可。

正解是行列同时分块后 FFT。

时间复杂度 \(\mathcal O\left(\dfrac{n^2+nq}{\omega}\right)\)

代码:

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int MAXN=1e5+5,MAXQ=1675,MOD=998244353,B=60,P=(1<<15)-1;
struct seg { int x,y; };
int n,m,zx[MAXN],zy[MAXN],s[MAXQ*4][P+5];
vector <seg> f[MAXN],g[MAXN],h[MAXN*2],w,ig[MAXN],ih[MAXN];
ull a[MAXQ],b[MAXQ],c[MAXQ],pw[B+5];
void upd(vector<seg>&e) {sort(e.begin(),e.end(),[&](auto i,auto j){ return i.x<j.x; }),w.clear();for(auto i:e) {if(w.empty()||w.back().y<i.x-1) w.push_back(i);else w.back().y=max(w.back().y,i.y);}
}
signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=B;++i) pw[i]=pw[i-1]<<1|1;for(int i=1;i<=n;++i) cin>>zx[i];for(int i=1;i<=n;++i) cin>>zy[i];for(int lx,ly,rx,ry;m--;) {cin>>lx>>ly>>rx>>ry;if(lx>rx||(lx==rx&&ly>ry)) swap(lx,rx),swap(ly,ry);if(lx==rx) f[lx].push_back({ly,ry});else if(ly==ry) g[ly].push_back({lx,rx});else h[ly+n-lx].push_back({lx,rx});}for(int i=1;i<=n;++i) {upd(f[i]),f[i].swap(w),upd(g[i]);for(auto j:w) ig[j.x].push_back({i,1}),ig[j.y+1].push_back({i,0});}for(int i=-n;i<=n;++i) {upd(h[i+n]);for(auto j:w) ih[j.x].push_back({j.x+i,1}),ih[j.y+1].push_back({j.y+1+i,0});}for(int i=0;i<=n/15;++i) {for(int j=0;j<15;++j) s[i][1<<j]=zy[i*15+j];for(int j=1;j<=P;++j) s[i][j]=(s[i][j&-j]+s[i][j&(j-1)])%MOD;}int q=n/B,ans=0; const ull U=pw[B];for(int i=1;i<=n;++i) {memset(a,0,sizeof(a));for(auto j:f[i]) {int l=j.x/B,r=j.y/B;if(l==r) a[l]^=pw[j.y%B+1]^pw[j.x%B];else {a[l]^=U^pw[j.x%B],a[r]^=pw[j.y%B+1];if(r-l>=2) memset(a+l+1,0xff,(r-l-1)<<3);}}for(auto j:ig[i]) {if(j.y) b[j.x/B]|=1ll<<j.x%B;else b[j.x/B]&=~(1ll<<j.x%B);}for(auto j:ih[i]) {if(j.y) c[j.x/B]|=1ll<<j.x%B;else c[j.x/B]&=~(1ll<<j.x%B);}ull z=0,d;for(int x=0;x<=q;++x) if(d=a[x]&b[x]&c[x]) {z+=0ll+s[x<<2][d&P]+s[x<<2|1][d>>15&P]+s[x<<2|2][d>>30&P]+s[x<<2|3][d>>45&P];}ans=(ans+z%MOD*zx[i])%MOD;for(int x=q;~x;--x) c[x+1]|=c[x]>>59&1,c[x]=c[x]<<1&U;}cout<<ans<<"\n";return 0;
}

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

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

相关文章

简单快速理解遗传算法

简单快速理解遗传算法<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, init…

浙江网站怎么做推广网站编辑器介绍

【VBA解决方案】全自动将Word中的文本公式转换为标准公式 写在最前面VBA代码全自动方法将md文档导出为word代码如何运行VBA代码注意事项 一些如何实现的回忆记录步骤解析手动将文本转换为Word公式代码逻辑步骤设想代码解析代码解释总结 其他背景介绍应用场景VBA脚本介绍如何使用…

wordpress 不能上传seo怎么优化一个网站

摘要&#xff1a;开发中经常需要校验用户提交的值是否满足要求&#xff0c;Valid可用于方法参数、返回值等的验证&#xff0c;但是对于参数为列表时无效&#xff0c;此处记录几种对列表进行验证的方法 Valid 注解通常用于验证单个对象的字段&#xff0c;而不是整个列表。仅添加…

网站建设模拟实验报告湛江网站制作费用

题目2&#xff1a; EXTERNAL关键字的作用&#xff1f;[多选] A、EXTERNAL关键字可以让用户创建一个外部表 B、创建外部表时&#xff0c;可以不加EXTERNAL关键字 C、通过EXTERNAL创建的外部表只删除元数据&#xff0c;不删除数据 D、不加EXTERNAL的时候&#xff0c;默认创建内…

wordpress导航菜单居中百度关键词优化首选667seo

算法&#xff1a; 这道题可以用回溯&#xff0c;但是可能会超时 可以用背包问题解决&#xff1a; 物品&#xff1a;单词 背包&#xff1a;字符串&#xff1a; 单词能否组成字符串s&#xff0c;就是问物品能不能把背包装满。 拆分时可以重复使用字典中的单词&#xff0c;就…

天津广告公司网站建设建网站手续

python的redis库查询返回的值默认是返回字节串&#xff0c;可以在redis.Redis()方法中通过设置decode_responses参数&#xff0c;让返回值直接是字符串&#xff1b; 查询返回字节串是因为Redis()方法中decode_responses默认值是False&#xff1a; 设置decode_responses为True就…

建立网站项目网络游戏排行榜2022

实现一个一遍扫描的编译前端&#xff0c;将简化高级语言的部分语法成分&#xff08;含赋值语句、分支语句、循环语句等&#xff09;翻译成四元式&#xff08;或三地址代码&#xff09;&#xff0c;还要求有合理的语法出错报错和错误恢复功能。 测试样例 beginwhile a<b do…

WordPress文章设置固定链接或永久链接 - 教程

WordPress文章设置固定链接或永久链接 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &qu…

垫江网站建设哪家好产品招商网

一、安装完虚拟机后的操作 第一步: 第二步&#xff1a;分配的内存大一下&#xff0c;处理器多些 第三步&#xff1a;打开虚拟化 打开虚拟机、安装KVM 一般企业如果使用kvm虚拟化平台&#xff0c;都会把物理服务器装成Centos的操作系统&#xff0c;然后装上kvm&#xff0c;创建…

尚义住房和城乡规划建设局网站广告设计专业前景

文章目录 嫌啰嗦直接看源码Q5 :PyTorch on CIFAR-10three_layer_convnet题面解析代码输出 Training a ConvNet题面解析代码输出 ThreeLayerConvNet题面解析代码输出 Train a Three-Layer ConvNet题面解析代码输出 Sequential API: Three-Layer ConvNet题面解析代码输出 CIFAR-1…

个人用云计算学习笔记 --15. (Linux 系统启动原理、Linux 防火墙管理)) - 实践

个人用云计算学习笔记 --15. (Linux 系统启动原理、Linux 防火墙管理)) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-f…

给小孩出数学题

给小孩出数学题import java.util.Random; import java.util.Scanner; public class math_problems { public static void main(String[] args){ Random r=new Random(); Scanner sc=new Scanner(System.in); int probl…

dotnet项目编译运行

dotnet build - 基本构建 dotnet build PurestAdmin.Zero/PurestAdmin.Zero.csproj# 指定解决方案文件 dotnet build PurestAdmin.sln构建的常用参数 # 指定配置(Debug 或 Release) dotnet build --configuration Re…

linux virtualenv使用

在Linux系统中,virtualenv是一个用于创建虚拟环境的Python包。它允许你在不同的Python版本或不同的Python环境中安装和管理库。以下是如何在Linux中使用virtualenv的步骤:首先,确保你已经安装了Python。如果没有,请…

已有网站做google推广成都网站seo设计

0 软件开发人员自我成长 1 每天读2~3篇文章&#xff0c;可以行业趋势、技术类(和自己的工作有关的) 大厂技术博客科技资讯类&#xff1a;量子位、差评、新智元、无敌信息差 量子位、新智元经验分享、编程趋势、技术干活&#xff1a;程序员鱼皮、小林coding、java guide、程序…

实用指南:kafka详解

实用指南:kafka详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "…

06-基于FPGA和LTC2308的数字电压表设计-ModelSim仿真与Matlab模拟信号产生 - 详解

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

详细介绍:whisper-large-v3部署详细步骤,包括cpu和gpu方式,跟着做一次成功

详细介绍:whisper-large-v3部署详细步骤,包括cpu和gpu方式,跟着做一次成功2025-09-28 22:42 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-…

oracle_19c_ru_ojvm_upgrade.sh一键升级脚本分享

oracle_19c_ru_ojvm_upgrade.sh一键升级脚本分享2025-09-28 22:43 潇湘隐者 阅读(0) 评论(0) 收藏 举报oracle_19c_ru_ojvm_upgrade.sh脚本的初始版本来源于IT邦德的分享,使用原脚本时发现有一些bug,在我的环境中…