2026.1.9 题解
100+40+65=205,rk2。
(好好好倒数前二变成正数前二了是吧)。
A. 线段
超级大水题,对于每个 \([l_i,r_i]\),考虑分讨,分别有不相交、前半相交、后半相交、包含、被包含。第一种不管,后两种二维数点板子,前半相交考虑答案一定为 \((r_i-L)^k\) 的形式,这玩意可以通过二项式定理拆成 k 个二维数点板子,后半相交同理。时间复杂度 \(O(nk\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,p=1e9+7;
inline int qpow(int x,int y){
int re=1;
while(y){
if(y&1) re=1ll*re*x%p;
x=1ll*x*x%p,y>>=1;
}
return re;
}
inline void md(int &x,int y){
x+=y;
if(x>=p) x-=p;
}
int n,k,m,tot,ans[N],c[15][N],xs[15];
struct que{
int x,y,id;
}qu[N*2];
inline bool cmp1(que x,que y){
return x.x!=y.x?x.x>y.x:x.y!=y.y?x.y<y.y:x.id<y.id;
}
inline bool cmp2(que x,que y){
return x.x!=y.x?x.x<y.x:x.y!=y.y?x.y>y.y:x.id>y.id;
}
inline bool cmp3(que x,que y){
return x.x!=y.x?x.x<y.x:x.id>y.id;
}
inline bool cmp4(que x,que y){
return x.y!=y.y?x.y>y.y:x.id>y.id;
}
inline void add(int x,int v,int id=0){
for(;x<=n;x+=x&-x) md(c[id][x],v);
}
inline int sum(int x,int id=0){
int re=0;
for(;x;x-=x&-x) md(re,c[id][x]);
return re;
}
inline void clear(int id){
for(int i=1;i<=n;i++) c[id][i]=0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k>>m;
for(int i=0;i<=k;i++){
xs[i]=1;
for(int j=1;j<=i;j++)
xs[i]=xs[i]*(k-j+1ll)/j;
}
for(int i=1,l,r;i<=n;i++)
cin>>l>>r,qu[++tot]={l,r,0};
for(int i=1,l,r;i<=m;i++)
cin>>l>>r,qu[++tot]={l,r,i};
sort(qu+1,qu+tot+1,cmp1);
for(int i=1;i<=tot;i++){
if(qu[i].id) md(ans[qu[i].id],sum(qu[i].y));
else add(qu[i].y,qpow(qu[i].y-qu[i].x+1,k));
}
clear(0),sort(qu+1,qu+tot+1,cmp2);
for(int i=1;i<=tot;i++){
if(!qu[i].id) add(n-qu[i].y+1,1);
else md(ans[qu[i].id],1ll*sum(n-qu[i].y+1)*qpow(qu[i].y-qu[i].x+1,k)%p);
}
clear(0),sort(qu+1,qu+tot+1,cmp3);
for(int i=1;i<=tot;i++){
if(qu[i].id) for(int j=0;j<=k;j++)
md(ans[qu[i].id],1ll*xs[j]*(sum(qu[i].y-1,k-j)-sum(qu[i].x-1,k-j)+p)%p*qpow(p-qu[i].x+1,j)%p);
else for(int j=0;j<=k;j++) add(qu[i].y,qpow(qu[i].y,j),j);
}
for(int i=0;i<=k;i++) clear(i);
sort(qu+1,qu+tot+1,cmp4);
for(int i=1;i<=tot;i++){
if(qu[i].id) for(int j=0;j<=k;j++)
md(ans[qu[i].id],1ll*xs[j]*(sum(n-qu[i].x,j)-sum(n-qu[i].y,j)+p)%p*qpow(qu[i].y,k-j)%p);
else for(int j=0;j<=k;j++) add(n-qu[i].x+1,qpow(p-qu[i].x+1,j),j);
}
for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
return 0;
}
B. 机器人
首先一个出口只能过一个机器人,任意两个机器人路径不会合并,任意两个机器人路径不会相逆。
那这玩意相当于拿机器人和出口匹配,很容易想到网络流。而这玩意看着像是个最大流(看看是否所有机器人都能出去),考虑建模:
- dyc 法:对于每个格子建立上下左右四点,每个格子的四点连成一张完全图,然后左右连右左,下上连上下。
- dzb 法:对于每个格子建立横出入点、纵出入点和中转点,入点连向中转点,中转点连向出点,横出点连上下的横入点,纵出点连上下的纵入点。
时间复杂度远小于理论的 \(O(n^6)\)。
顺便再提一嘴,假如你想 lmx 一样,能写出状压 dp 过前 10 个点,你就可以采用 47 定律:
47 定律:本题后 10 个点只需要判断有没有机器人和出口不在一个连通块,若都在就输出 Yes,否则输出 No。
#include<bits/stdc++.h>
using namespace std;
const int N=105,M=5*N*N,K=17*N*N;
int T,n,m,a,b,ac[N],bc[N];
int k=1,h[M],to[K],nx[K],w[K];
int s,t,d[M],c[M],q[M],fs,ed;
char cc[N][N];
inline void add(int x,int y,int z){
nx[++k]=h[x],to[h[x]=k]=y,w[k]=z;
nx[++k]=h[y],to[h[y]=k]=x,w[k]=0;
}
inline int bfs(){
for(int i=s;i<=t;i++) c[i]=-1;
d[s]=h[s],q[fs=ed=1]=s,c[s]=0;
while(fs<=ed){
int x=q[fs++];
for(int i=h[x];i;i=nx[i])
if(w[i]>0&&c[to[i]]<0){
c[to[i]]=c[x]+1,d[to[i]]=h[to[i]],q[++ed]=to[i];
if(to[i]==t) return 1;
}
}
return 0;
}
inline int dfs(int x,int ans){
if(x==t) return ans;
int sum=ans;
for(int i=d[x],dl;i&∑i=nx[i]){
d[x]=i;
if(c[to[i]]==c[x]+1&&w[i]>0){
dl=dfs(to[i],min(sum,w[i]));
w[i]-=dl,sum-=dl,w[i^1]+=dl;
}
}
return ans-sum;
}
inline int dinic(){
int re=0;
while(bfs()) re+=dfs(s,1e9);
return re;
}
inline void solve(){
for(int i=s;i<=t;i++) h[i]=0;
cin>>n>>m>>a>>b,s=0,t=5*n*m+1,k=1;
for(int i=1;i<=n;i++) cin>>(cc[i]+1);
for(int i=1;i<=a;i++) cin>>ac[i],add(s,ac[i],1);
for(int i=1;i<=b;i++) cin>>bc[i],add(bc[i]+n*m*4-m,t,1);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(cc[i][j]==48){
add(i*m-m+j,i*m-m+j+n*m*2,1),add(i*m-m+j+n*m,i*m-m+j+n*m*2,1);
add(i*m-m+j+n*m*2,i*m-m+j+n*m*3,1),add(i*m-m+j+n*m*2,i*m-m+j+n*m*4,1);
if(cc[i-1][j]==48) add(i*m-m+j+n*m*3,i*m-m-m+j,1);
if(cc[i+1][j]==48) add(i*m-m+j+n*m*3,i*m+j,1);
if(cc[i][j-1]==48) add(i*m-m+j+n*m*4,i*m-m+j-1+n*m,1);
if(cc[i][j+1]==48) add(i*m-m+j+n*m*4,i*m-m+j+1+n*m,1);
}
cout<<(dinic()==a?"Yes\n":"No\n");
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--) solve();
return 0;
}
C. 我约等于世界
有神秘倒开爷场切了这道题。
显然对于每种质因数可以分开算,问题转化为求一个长为 \(n\) 的序列,序列的值表示当前人的坐标,假如让一个区间的人集合所需的最小总代价。经典题目,显然是取中位数。根据一些 \(trick\) 转化成对于每个分解位置 \(x\),\(\min(\sum[a_i\le x],\sum[a_i>x])\) 的值,最后再把它们全部加起来就行。由于 \(x\in[0,\log n]\),所以该问题只需要 \(O(len)\)。
这类题又是一个 \(trick\),设前一类数是 -1,后一类数是 1,设新序列的子区间和为 \(sum(l,r)\),则对于一个子区间,答案为 \(\frac{r-l+1-sum(l,r)}2\)。由于相邻位置前缀和的变化范围是 1 或 -1,所以假如把前缀和扔到桶里,序列长度是 \(O(len)\) 的,其中 \(len\) 为当前处理的区间大小。
但这样的话也不是办法,因为质因数种类数量级是 \(O(n\log V)\) 的。但我们发现实际上真正有效的位置其实也是 \(O(n\log V)\) 的。因为以其他位置为起点,中位数一定是 0,这种情况是好算的,所以只需要对于每种质因数,记录包含该质因数的位置,以及其没有被标记的前驱后继。把这些段拎出来跑就行了。这个可以用并查集求出来,时间复杂度 \(O(n\log^2n)\)。
这并不是本题的唯一解法。lmx 的解法是,若没有想到最后一步,考虑根号分治,时间复杂度就会变成 \(O(n\sqrt{n\log n}\log n)\),虽然看起来很大但根本跑不满,甚至可以碾压大部分双 \(\log\) 做法。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=3e6+5,p=1e9+7;
int n,tg[M],vs[N],vis[N],fa[N],st[N],tp,ans,sm[N<<1];
int tot,h[M],nx[M],id[M],num[M];
inline int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void solve(int l,int r){
int mx=0;
for(int j=l;j<=r;j++)
mx=max(mx,vs[j]),ans=(ans-vs[j]*(j-l+1ll)*(r-j+1)%p+p)%p;
for(int i=0;i<mx;i++){
sm[n+1]=1;
int mx=n+1,sum=n+1,re=0,nw=n+1;
for(int j=l;j<=r;j++){
mx=max(nw+=(vs[j]<=i?-1:1),mx);
sm[nw]++,sum=(sum+nw)%p;
}
for(int j=mx,nw=r-l+2;sm[j];j--){
sum=(sum-1ll*sm[j]*j%p+p)%p,nw-=sm[j];
re=(re+(1ll*nw*j-sum+p)%p*sm[j])%p,sm[j]=0;
}
int m=r-l+1;
ans=(ans+((m+1ll)*(m+1)*m/2-m*(m+1ll)*(2*m+1)/6-re+p)%p*(p+1)/2)%p;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1,a;i<=n;i++){
cin>>a,fa[i]=i;
for(int j=2,re;j*j<=a;j++) if(a%j==0){
re=0,id[++tot]=i;
while(a%j==0) re++,a/=j;
nx[tot]=h[j],num[h[j]=tot]=re;
}
if(a>1) id[++tot]=i,nx[tot]=h[a],num[h[a]=tot]=1;
}
for(int i=2;i<=1e6;i++) if(h[i]){
while(tp) fa[st[tp]]=st[tp],vs[st[tp--]]=0;
for(int j=h[i],x;j;j=nx[j]){
vs[x=id[j]]=num[j];
vis[st[++tp]=x]=1;
ans=(ans+x*(n-x+1ll)*vs[x])%p;
}
for(int j=h[i],x;j;j=nx[j]){
x=id[j];
while(vis[x]){
x=find(fa[x])-1;
if(x) fa[x+1]=fa[x];
}
if(x) vis[x]=1,st[++tp]=x;
}
for(int i=1;i<=tp;i++)
fa[st[i]]=(vis[st[i]+1]?st[i]+1:st[i]);
for(int j=h[i],x;j;j=nx[j]){
x=id[j];
while(vis[x]){
x=find(fa[x])+1;
if(x<=n) fa[x-1]=fa[x];
}
if(x<=n) vis[x]=1,st[++tp]=x;
}
for(int i=1;i<=tp;i++) if(vis[st[i]]){
int l=st[i],r=st[i];
while(vis[l-1]) vis[--l]=0;
while(vis[r+1]) vis[++r]=0;
vis[st[i]]=0,solve(l,r);
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号