CSP 后多校十五
A. 法阵
容易发现填放 \(1\) 的位置一定是两个类似三角形的地方.
问题可以巧妙的转化为放 \(1\) 的地方和放 \(0\) 的地方形成了不同的连通块,
且放 \(1\) 连通块的个数和放 \(0\) 连通块的个数都不超过 \(2\).
于是走格子计数就好.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define int long long
#define lf long double
#define pb push_back
#define kap make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(y))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
while(!isdigit(ch)) cit|=(ch=='-'),ch=getchar();
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?(-w):w;
};
}using namespace BSS;
const int mod=998244353,N=4e3+21;
int m,n,ans,sum;
int C[N][N];
signed main(){
File(magic);
n=read(),m=read();
for(int i=0;i<=4e3;i++){
C[i][0]=1;
for(int j=1;j<=i;j++){
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
for(int i=1;i<n;i++){
sum=0;
for(int j=1;j<m;j++){
sum=(sum+C[i-1+m-j][i-1]*C[i+j-1][j-1]%mod)%mod;
ans=(ans+sum*C[n-i-1+j][j]%mod*C[n-i+m-j-1][m-j-1]%mod)%mod;
}
}
int z=m; m=n,n=z;
for(int i=1;i<n;i++){
sum=0;
for(int j=1;j<m;j++){
ans=(ans+sum*C[n-i-1+j][j]%mod*C[n-i+m-j-1][m-j-1]%mod)%mod;
sum=(sum+C[i-1+m-j][i-1]*C[i+j-1][j-1]%mod)%mod;
}
}
printf("%lld\n",(ans<<1ll)%mod),exit(0);
}
B. 连通块
很自然的就觉得应该倒着做.
树上距离某个点最远的点一定是直径的端点之一.
所以并查集合并的时候维护一下直径端点就可以了.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define int long long
#define lf long double
#define pb push_back
#define kap make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(y))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
while(!isdigit(ch)) cit|=(ch=='-'),ch=getchar();
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?(-w):w;
};
}using namespace BSS;
#define y1 asas
#define y2 sasa
const int N=2e5+21;
int m,n,ts,rt,lg2;
int head[N],fa[N],dep[N],vis[N],sep[N];
int d[N][2],st[N][25];
vector<int> ans,son[N];
struct I { int opt,x; } p[N];
struct II { int u,v,w,nxt; } e[N<<1];
int fond(int x){ return fa[x]==x ? x : fa[x]=fond(fa[x]); }
auto add=[](int u,int v)->void{
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
head[u]=ts;
};
void dfs(int u,int dad){
st[u][0]=dad,dep[u]=dep[dad]+1,fa[u]=u;
for(int i=1;i<=lg2 and st[u][i-1];i++){
st[u][i]=st[st[u][i-1]][i-1];
}
for(int i=head[u],v;i;i=e[i].nxt){
if(v=e[i].v,v==dad) continue;
dfs(v,u);
}
}
auto lca=[](int x,int y)->int{
if(dep[x]<dep[y]) swap(x,y);
for(int i=lg2;~i;i--){
if(dep[st[x][i]]>=dep[y]) x=st[x][i];
}
if(x==y) return x;
for(int i=lg2;~i;i--){
if(st[x][i]^st[y][i]) x=st[x][i],y=st[y][i];
}
return st[x][0];
};
auto getdis(int u,int v)->int{
return dep[u]+dep[v]-dep[lca(u,v)]*2;
};
void sch(int u,int dad){
son[rt].pb(u),fa[fond(u)]=rt;
sep[u]=sep[dad]+1,vis[u]=1;
for(int i=head[u],v;i;i=e[i].nxt){
if(v=e[i].v,e[i].w or v==dad) continue;
sch(v,u);
}
}
auto solve=[](int x)->void{
int u,v,w; rt=x,sch(x,0),u=0;
for(auto i : son[x]) if(sep[i]>sep[u]) u=i;
son[x].clear(),sch(u,0),v=0;
for(auto i : son[x]) if(sep[i]>sep[v]) v=i;
d[x][0]=u,d[x][1]=v;
};
signed main(){
File(block);
n=read(),m=read(),ts=1,lg2=log2(n)+1;
int u,v,w,x,y,z,x1,x2,y1,y2,w1,w2,w3,w4,w5,w6;
for(int i=2;i<=n;i++){
u=read(),v=read(),add(u,v),add(v,u);
}
for(int i=1;i<=m;i++){
p[i].opt=read(),p[i].x=read();
if(p[i].opt&1) e[p[i].x*2].w=1,e[p[i].x*2+1].w=1;
}
for(int i=1;i<=n;i++) fa[i]=i;
dfs(1,0);
for(int i=1;i<=n;i++){
if(!vis[i]) solve(i);
}
for(int i=m;i>=1;i--){
x=p[i].x;
if(p[i].opt&1){
u=fond(e[x*2].u),v=fond(e[x*2].v),fa[u]=v;
x1=d[u][0],y1=d[u][1],x2=d[v][0],y2=d[v][1];
w1=getdis(x1,y1),w2=getdis(x1,x2),w3=getdis(x1,y2);
w4=getdis(y1,x2),w5=getdis(y1,y2),w6=getdis(x2,y2);
w=max(max(w1,w2),max(w3,w4)),w=max(w,max(w5,w6));
if(w==w1) d[v][0]=x1,d[v][1]=y1;
else if(w==w2) d[v][0]=x1,d[v][1]=x2;
else if(w==w3) d[v][0]=x1,d[v][1]=y2;
else if(w==w4) d[v][0]=y1,d[v][1]=x2;
else if(w==w5) d[v][0]=y1,d[v][1]=y2;
else if(w==w6) d[v][0]=x2,d[v][1]=y2;
}
else{
u=d[fond(x)][0],v=d[fond(x)][1];
ans.pb(max(getdis(x,u),getdis(x,v)));
}
}
for(int i=ans.size()-1;~i;i--) printf("%lld\n",ans[i]);
exit(0);
}
C. 军队
不难想到要差分,于是线段树维护前 \(k\) 小即可.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
// #define int long long
#define lf long double
#define pb push_back
#define kap make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(y))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
while(!isdigit(ch)) cit|=(ch=='-'),ch=getchar();
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?(-w):w;
};
}using namespace BSS;
#define LL long long
#define ls (x<<1)
#define rs (x<<1|1)
#define y1 asas
#define y2 sasa
#define fi first
#define se second
const int N=3e5+21,inf=1e9;
int m,n,s,t,ops;
LL pre[N][2];
pair<int,int> sx[N];
vector<pair<int,int> > inc[N],del[N];
struct I { int is,lzy,sum; pair<int,int> w[12]; } tr[N<<2];
auto ksm=[](int a,int b,int w=1)->int{
for(;b;b>>=1,a=a*a) if(b&1) w=w*a;
return w;
};
void build(int x,int l,int r){
tr[x].is=1,tr[x].w[1].fi=0,tr[x].w[1].se=r-l+1,tr[x].sum=(r-l+1)*(t>0);
if(l==r) return ; int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
}
auto put=[](int x)->void{
cout<<"x:"<<x<<' '<<tr[x].is<<' '<<tr[x].w[1].fi<<' '<<tr[x].w[1].se<<' '<<tr[x].w[2].fi<<" "<<tr[x].w[2].se<<endl;
};
auto getval=[](int x,int w)->void{
tr[x].sum=0,tr[x].lzy+=w;
for(int i=1,lmi=tr[x].is;i<=lmi;i++) tr[x].w[i].fi+=w;
for(int i=1,lmi=tr[x].is;i<=lmi and tr[x].w[i].fi<t;i++) tr[x].sum+=tr[x].w[i].se;
// cout<<"x and w:"<<x<<' '<<w<<" "<<tr[x].is<<' '<<tr[x].w[tr[x].is].fi<<endl;
};
auto spread=[](int x)->void{
int &lzy=tr[x].lzy; if(!lzy) return ;
getval(ls,lzy),getval(rs,lzy),lzy=0;
};
auto pushup=[](int x)->void{
int i=1,j=1,k=0,lmi=tr[ls].is,lmj=tr[rs].is;
tr[x].sum=0,tr[x].w[0].fi=-1;
while(k<t and (i<=lmi or j<=lmj)){
while(k<t and i<=lmi and (tr[ls].w[i].fi<=tr[rs].w[j].fi or j>lmj)){
if(tr[ls].w[i].fi==tr[x].w[k].fi) tr[x].w[k].se+=tr[ls].w[i++].se;
else tr[x].w[++k]=tr[ls].w[i++];
}
if(k<t and j<=lmj){
if(tr[rs].w[j].fi==tr[x].w[k].fi) tr[x].w[k].se+=tr[rs].w[j++].se;
else tr[x].w[++k]=tr[rs].w[j++];
}
}
tr[x].is=k;
while(tr[ls].w[i].fi==tr[x].w[k].fi and i<=lmi) tr[x].w[k].se+=tr[ls].w[i++].se;
while(tr[rs].w[j].fi==tr[x].w[k].fi and j<=lmj) tr[x].w[k].se+=tr[rs].w[j++].se;
for(i=1;i<=k and tr[x].w[i].fi<t;i++) tr[x].sum+=tr[x].w[i].se;
// put(x),put(ls),put(rs);
};
void upd(int x,int l,int r,int ql,int qr,int w){
// cout<<x<<' '<<l<<' '<<r<<' '<<ql<<' '<<qr<<endl;
if(l>=ql and r<=qr) return getval(x,w),void();
int mid=(l+r)>>1; spread(x);
if(ql<=mid) upd(ls,l,mid,ql,qr,w);
if(qr>mid) upd(rs,mid+1,r,ql,qr,w);
pushup(x);
}
signed main(){
File(army);
n=read(),m=read(),s=read(),t=read(),ops=read();
int w,x,y,z,x1,y1,x2,y2;
for(int i=1;i<=s;i++){
x1=read(),y1=read(),x2=read(),y2=read();
inc[x1].pb(kap(y1,y2)),del[x2+1].pb(kap(y1,y2));
}
build(1,1,m);
for(int i=1;i<=n;i++){
for(auto j : inc[i]) upd(1,1,m,j.fi,j.se,1);
for(auto j : del[i]) upd(1,1,m,j.fi,j.se,-1);
sx[i].se=tr[1].sum,sx[i].fi=min(tr[1].sum,m-tr[1].sum);
// cout<<"sum:"<<tr[1].sum<<endl;
}
sort(sx+1,sx+1+n,[](pair<int,int> i,pair<int,int> j){ return i.fi>j.fi; });
for(int i=1;i<=n;i++) pre[i][0]=pre[i-1][0]+1ll*sx[i].fi,pre[i][1]=pre[i-1][1]+1ll*sx[i].fi*sx[i].fi;
sort(sx+1,sx+1+n);
while(ops--){
x=read(),y=read(),z=lb(sx+1,sx+1+n,kap(y/2,0))-sx-1;
int tmp=n-z; LL res=1ll*(y/2ll*(y-y/2ll))*min(x,tmp);
if(x>tmp) res+=(pre[x][0]-pre[tmp][0])*y-(pre[x][1]-pre[tmp][1]);
printf("%lld\n",res);
}
exit(0);
}
D. 棋盘
暴力的做法就是 \(O(nq^2)\) 的 \(dp\).
想办法少去枚举一些重复的信息.
观察数据范围,发现复杂度最多可以写 \(O(qn^2)\),或者带上个 \(log\).
于是不难想到每次都要利用之前的 \(dp\),但是每次删除和添加的操作又需要维护.
这个时候需要观察题目的性质,仔细思考可以知道这个马从 \((i,j)\) 点跳跃到 \((x,y)\) 点的过程是可逆的.
过程可逆,也就意味着方案数相同.
于是可以选择维护两个类似于对顶堆的东西.
\(dp_{i,j,x,y}\) 表示 从 \((x,y)\) 跳到 \((i,j)\) 的方案数.
考虑优化,发现 \(x\) 可以直接设为堆顶的位置 \(mid\),于是 \(dp_{i,j,y}\) 表示 从 \((mid,y)\) 跳到 \((i,j)\) 的方案数.
具体实现,针对向上还是向下,是否跨过 \(mid\) 进行讨论.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define int long long
#define lf long double
#define pb push_back
#define kap make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(y))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
while(!isdigit(ch)) cit|=(ch=='-'),ch=getchar();
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?(-w):w;
};
}using namespace BSS;
const int N=21,Q=1e5+21,mod=998244353;
char ch[N];
int m,n,l,r,mid,ops,ans;
int ma[Q][N];
int dp[2][Q][N][N];
auto inc=[](int &i,int j){ i+=j; i=i-(i>=mod)*mod; };
auto calc=[](int op,int x,int y,int z)->void{
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!ma[x][i]) continue;
if(i>2) inc(dp[op][x][i][j],dp[op][y][i-2][j]);
if(i>1) inc(dp[op][x][i][j],dp[op][z][i-1][j]);
if(i+2<=n) inc(dp[op][x][i][j],dp[op][y][i+2][j]);
if(i+1<=n) inc(dp[op][x][i][j],dp[op][z][i+1][j]);
}
}
};
auto rebuild=[]()->void{
mid=r;
for(int i=l;i<=mid;i++) Fill(dp[0][i],0);
for(int j=1;j<=n;j++) dp[0][mid][j][j]=ma[mid][j];
for(int i=mid-1;i>=l;i--) calc(0,i,i+1,((i==mid-1) ? 0 : i+2));
if(l==r) return ;
for(int i=l;i<mid;i++) Fill(dp[1][i],0);
for(int j=1;j<=n;j++) dp[1][mid-1][j][j]=ma[mid-1][j];
for(int i=mid-2;i>=l;i--) calc(1,i,i+1,((i==mid-2) ? 0 : i+2));
};
auto Work=[]()->void{
scanf("%s",ch+1);
if(ch[1]=='A'){
r++,scanf("%s",ch+1);
for(int i=1;i<=n;i++) ma[r][i]=(ch[i]=='.');
if(l==r) return rebuild(),void();
if(r==mid+1){
calc(0,r,r-1,0);
for(int i=1;i<=n;i++) dp[1][r][i][i]=ma[r][i];
}
else{
calc(0,r,r-1,r-2);
calc(1,r,r-1,((r-2>mid) ? r-2 : 0));
}
}
else if(ch[1]=='D'){
l++; if(l>mid and mid<r) rebuild();
}
else if(ch[1]=='Q'){
ans=0; int x=read(),y=read();
if(!(ma[l][x] and ma[r][y])) return puts("0"),void();
for(int i=1;i<=n;i++) inc(ans,dp[0][l][x][i]*dp[0][r][y][i]%mod);
// cout<<"ans:"<<ans<<'\n';
if(l<mid and r>mid){
for(int i=1;i<=n;i++){
if(i>1) inc(ans,dp[1][l][x][i]*dp[1][r][y][i-1]%mod);
if(i+1<=n) inc(ans,dp[1][l][x][i]*dp[1][r][y][i+1]%mod);
}
}
printf("%lld\n",ans);
}
};
signed main(){
File(chess);
n=read(),l=1,r=0;
for(int Ts=read();Ts;Ts--) Work();
exit(0);
}
总结
这次考试又是沉迷于做一道题难以自拔,其实 \(T2\)、\(T3\) 都比较简单.
另外能用数学知识解决的就不要老想着 \(dp\).