【比賽記錄】2025CSP-S模擬賽11

A. 異或
\(r+l=n+1\) 的特殊性質提示一個做法:修改時只在頂點標記,用兩個矩陣分別記錄向下傳遞的數和向右下傳遞的數。于是一個小三角就等于一個大三角減掉一個小三角再減掉一個矩形。這兩個三角也都是到底的,可以用如上方法計算;矩形直接做差分即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e3+5;
int n,m;
ll a[maxn][maxn],b[maxn][maxn],d[maxn][maxn];
int main(){
// freopen("xor2.in","r",stdin);
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
while(m--){
// puts("666");
int r,c,l,s;
cin>>r>>c>>l>>s;
a[r][c]+=s,b[r][c]+=s;
a[r+l][c+l]-=s,b[r+l][c+l]-=s;
d[r+l][c]-=s,d[r+l][c+l]+=s;
// puts("777");
}
// puts("666");
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i+1][j+1]+=a[i][j];
a[i+1][j]+=b[i][j];
b[i+1][j]+=b[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1];
}
}
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans^=a[i][j]+d[i][j];
}
}
cout<<ans;
return 0;
}
}
int main(){return asbt::main();}
B. 游戲
考慮如果 Alice 每次都刪去元素更多的那一半,則他最多操作 \(\log n\) 次就刪完了,算上先后手就是 \(2\times\log n\) 次后答案小于等于 \(0\)。對于 Bob 也是類似的,即 \(2\times\log n\) 次后答案大于等于 \(0\)。于是對于 \(m>2\times\log n\) 的情況直接輸出 \(0\) 即可,剩下的情況去做暴搜,時間復雜度為 \(O(nm)\)。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,a[maxn],b[maxn],hp[maxn];
il int dfs(int x,int l,int r){
if(l>r){
return 0;
}
if(x>m){
int res=0;
for(int i=l;i<=r;i++){
res+=a[i];
}
return res;
}
int pl=l,pr=r;
for(int i=l;i<=r;i++){
hp[a[i]%b[x]?pl++:pr--]=a[i];
}
for(int i=l;i<=r;i++){
a[i]=hp[i];
}
return x&1?min(dfs(x+1,l,pr),dfs(x+1,pl,r)):max(dfs(x+1,l,pr),dfs(x+1,pl,r));
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i];
}
if(m>2*log2(n)){
cout<<0;
return 0;
}
cout<<dfs(1,1,n);
return 0;
}
}
signed main(){return asbt::main();}
C. 連通塊
首先邊數比較爆炸,考慮優(yōu)化建圖。對于每個 \(a_i\),求出它所有的由 \(2\) 個質數相乘得到的因子,將 \(i\) 于這些點連邊即可(對于每個 \(a_i\) 這樣的點最多有 \(36\) 個)。
可以發(fā)現的一個性質是,那個刪掉的點必然在最大的連通塊里。我們要在連通塊中找到刪掉一個點后最大的子樹,那么這就可以用圓方樹來做。實際上是不用建出圓方樹的,直接在 tarjan 的過程中統(tǒng)計即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=4e6+5,maxm=1e7+5;
int T,n,prn,prm[maxm],tot,mpf[maxm];
int hd[maxn],enm,hp1[15],hp2[15],ID[maxm];
int cnt,mx1,mx2,mp1,mp2,dfn[maxn],low[maxn],stk[maxn],top,ans,sz[maxn];
bool npr[maxm],vis[maxn];
struct{
int v,nxt;
}e[maxn<<1];
il void addedge(int u,int v){
e[++enm].v=v;
e[enm].nxt=hd[u];
hd[u]=enm;
}
il void Addedge(int u,int x){
// cout<<u<<" "<<x<<"\n";
if(!ID[x]){
ID[x]=++tot;
}
int v=ID[x];
addedge(u,v);
addedge(v,u);
}
il void euler(int x=1e7){
for(int i=2;i<=x;i++){
if(!npr[i]){
prm[++prn]=i;
mpf[i]=i;
}
for(int j=1;j<=prn&&i*1ll*prm[j]<=x;j++){
npr[i*prm[j]]=1;
mpf[i*prm[j]]=prm[j];
if(i%prm[j]==0){
break;
}
}
}
}
il void dfs(int u){
vis[u]=1;
if(u<=n){
cnt++;
}
for(int i=hd[u],v;i;i=e[i].nxt){
v=e[i].v;
if(!vis[v]){
dfs(v);
}
}
}
il void tarjan(int u,int fa){
dfn[u]=low[u]=++cnt;
stk[++top]=u,sz[u]=u<=n;
int chl=0,res=0;
bool ged=0;
for(int i=hd[u],v;i;i=e[i].nxt){
v=e[i].v;
if(v==fa){
continue;
}
if(!dfn[v]){
tarjan(v,u);
chl++;
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
if(fa||chl>1){
ged=1;
}
int sum=0,x=0;
do{
x=stk[top--];
sum+=sz[x],sz[u]+=sz[x];
}while(x!=v);
res=max(res,sum);
}
}
else{
low[u]=min(low[u],dfn[v]);
}
}
if(u<=n){
// cout<<u<<"\n";
if(ged){
ans=min(ans,max(mx1-sz[u],res));
}
else{
ans=min(ans,mx1-1);
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
euler();
cin>>T;
while(T--){
cin>>n;
tot=n,enm=1;
for(int i=1,x;i<=n;i++){
cin>>x;
int cnt=0;
while(mpf[x]){
hp1[++cnt]=mpf[x];
while(x%hp1[cnt]==0){
x/=hp1[cnt];
hp2[cnt]++;
}
}
for(int j=1;j<=cnt;j++){
if(hp2[j]>1){
Addedge(i,hp1[j]*hp1[j]);
}
for(int k=j+1;k<=cnt;k++){
Addedge(i,hp1[j]*hp1[k]);
}
}
for(int j=1;j<=cnt;j++){
hp1[j]=hp2[j]=0;
}
}
mx1=mx2=mp1=mp2=0;
for(int i=1;i<=tot;i++){
if(!vis[i]){
cnt=0;
dfs(i);
if(cnt>=mx1){
mx2=mx1,mp2=mp1;
mx1=cnt,mp1=i;
}
else if(cnt>=mx2){
mx2=cnt,mp2=i;
}
}
}
cnt=top=0,ans=mx1;
tarjan(mp1,0);
cout<<max(ans,mx2)<<"\n";
for(int i=1;i<=tot;i++){
dfn[i]=low[i]=hd[i]=vis[i]=0;
}
for(int i=1;i<=1e7;i++){
ID[i]=0;
}
} // clear
return 0;
}
}
int main(){return asbt::main();}
D. 公交路線
我們考慮對于每個點算出以它為某個端點的答案,相加得到總方案數(需要除以 \(4\))。
記 \(f_u\) 表示 \(u\) 的子樹內經過 \(u\) 的路徑數,\(g_u\) 表示所有經過 \(u\) 的路徑數,二者可以對每種顏色建虛樹,然后樹上差分求出。然后再求一個 \(f\) 的根鏈和 \(pf\)。
對于一條路徑 \(u=u_1,u_2,\dots,u_p,w,v_q,v_{q-1},\dots,v_1=v\),其中 \(w\) 為 lca,與其相交的路徑數即為 \(\sum_{i=1}^{p}f_i+\sum_{i=1}^{q}f_i+g_w\),即 \(pf_u+pf_v-2\times pf_w+g_w\)。于是對于 \(u\),它的答案即為 \(\sum\limits_{c_v=c_u}(\sum_{i=1}^{n}f_i-pf_u+pf_v-2\times pf_w+g_w)\)。其中前兩項是好求的,剩下的三項再建虛樹,上傳到 \(w\) 上再下傳即可。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,k,sf,c[maxn],f[maxn],pf[maxn],g[maxn],cg[maxn],ans[maxn];
vector<int> e[maxn],cun[maxn];
namespace LCA{
int dfn[maxn],oula[maxn<<1],cnt,idx[maxn<<1];
il void dfs(int u,int fa){
dfn[u]=++cnt;
oula[cnt]=cnt;
idx[cnt]=u;
for(int v:e[u]){
if(v==fa){
continue;
}
dfs(v,u);
oula[++cnt]=dfn[u];
}
}
struct{
int Log[maxn<<1],st[maxn<<1][22];
il void build(){
for(int i=2;i<=cnt;i++){
Log[i]=Log[i>>1]+1;
}
for(int i=1;i<=cnt;i++){
st[i][0]=oula[i];
}
for(int j=1;j<=Log[cnt];j++){
for(int i=1;i+(1<<j)-1<=cnt;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
il int query(int l,int r){
int p=Log[r-l+1];
return min(st[l][p],st[r-(1<<p)+1][p]);
}
}ST;
il void init(){
dfs(1,0),ST.build();
}
il int lca(int u,int v){
if(dfn[u]>dfn[v]){
swap(u,v);
}
// cout<<ST.query(dfn[u],dfn[v])<<"\n";
return idx[ST.query(dfn[u],dfn[v])];
}
}
using LCA::dfn;
using LCA::lca;
namespace vtr{
int col,sum,tot,hp[maxn<<1],sz[maxn],spf[maxn];
vector<int> e[maxn];
il bool cmp(int x,int y){
return dfn[x]<dfn[y];
}
il void build(){
tot=0;
for(int u:cun[col]){
hp[++tot]=u;
}
hp[++tot]=1;
sort(hp+1,hp+tot+1,cmp);
for(int i=1;i<=sum;i++){
hp[++tot]=lca(hp[i],hp[i+1]);
}
sort(hp+1,hp+tot+1,cmp);
tot=unique(hp+1,hp+tot+1)-hp-1;
for(int i=1;i<tot;i++){
int x=lca(hp[i],hp[i+1]),y=hp[i+1];
e[x].pb(y);
}
}
il void clear(){
for(int i=1;i<=tot;i++){
e[hp[i]].clear();
}
}
il void dfs1(int u,int fa){
sz[u]=c[u]==col;
for(int v:e[u]){
dfs1(v,u);
f[u]+=sz[v]*sz[u];
sz[u]+=sz[v];
}
int tmp=sz[u]*(sum-sz[u]);
cg[u]+=tmp,cg[fa]-=tmp;
}
il void dfs2(int u){
if(c[u]==col){
sz[u]=1,spf[u]=pf[u];
}
else{
sz[u]=spf[u]=0;
}
for(int v:e[u]){
dfs2(v);
sz[u]+=sz[v],spf[u]+=spf[v];
}
}
il void dfs3(int u,int w){
int aisi=0,aifu=0;
for(int v:e[u]){
aisi+=sz[v];
aifu+=spf[v];
}
if(c[u]==col){
ans[u]+=w+2*pf[u]*aisi-g[u]*aisi-aifu;
aisi++,aifu+=pf[u];
}
for(int v:e[u]){
aisi-=sz[v],aifu-=spf[v];
dfs3(v,w+2*pf[u]*aisi-g[u]*aisi-aifu);
aisi+=sz[v],aifu+=spf[v];
}
}
il void work1(int x){
col=x,sum=cun[x].size();
build();
dfs1(1,0);
clear(); // !!!
}
il void work2(int x){
col=x,sum=cun[x].size();
for(int u:cun[x]){
ans[u]+=(sf-pf[u])*(sum-1);
}
build();
dfs2(1);
dfs3(1,0);
clear();
}
}
il void dfs(int u,int fa){
pf[u]=pf[fa]+f[u];
sf+=f[u];
for(int v:e[u]){
if(v==fa){
continue;
}
dfs(v,u);
cg[u]+=cg[v];
}
g[u]=f[u]+cg[u];
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
cin>>c[i];
cun[c[i]].pb(i);
}
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
LCA::init();
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++){
// cout<<i<<" "<<j<<" "<<lca(i,j)<<"\n";
// }
// }
// cout<<"---------------------------------------\n";
for(int i=1;i<=k;i++){
if(cun[i].empty()){
continue;
}
vtr::work1(i);
}
dfs(1,0);
// for(int i=1;i<=n;i++){
// cout<<i<<" "<<f[i]<<" "<<g[i]<<"\n";
// }
for(int i=1;i<=k;i++){
if(cun[i].size()){
vtr::work2(i);
}
}
int res=0;
for(int i=1;i<=n;i++){
res+=ans[i];
}
res>>=2;
cout<<res<<"\n";
while(m--){
int u;
cin>>u;
cout<<res-ans[u]<<"\n";
}
return 0;
}
}
signed main(){return asbt::main();}

浙公網安備 33010602011771號