2021.11.16考试总结[冲刺NOIP模拟31]
前两天日均切前两题(签到水平),一开始开题的时候发现T1不会切,整个人都慌了。。好像会80的DP,但样例没过,人彻底傻了。
看T2,这种树上玄学问题好像都很不可做,只想得到个删边变加边,也没仔细想就跳了。
于是开考一个多小时只有个T2暴力,有点离谱。感觉这场要凉。。感觉T3T4切有点不大现实,就回去看T1,发现有情况算少了,nm互换再做一遍,竟然就把样例过了。感觉这题有点吊,抱着别人可能80都没有的想法,心态好了些。
看T3,感觉询问可以离线搞搞,但不会扫描线求k层覆盖,写了个剪枝暴力,测大样例user0.5s,real3s,有些不知所措。。T4没时间了,随便写了暴力。突然感觉考的还行?
得分80+60+90+10,结果T2是最水的题,T4暴力挂了,T3跑得比正解都快,但nq不同阶RE了。。拿了校内rk1?有点离谱。。
T1 法阵
发现合法情况是左右或上下各有一个矩形一样的连通块,且它们互不连通。于是枚举它们间的断线(?,可以用组合数把方案数算出来,再前缀和优化一下就好。
我知道很敷衍,但真不会讲。。建议看lg题解
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define int long long
int read(){
int x=0,f=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char Ch[50];
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
const int NN=4010,mod=998244353;
int n,m,sum,ans;
namespace Combinatorics{
int fac[NN],inv[NN];
int C(int x,int y){ return x<0||y<0||x<y?0:fac[x]*inv[y]%mod*inv[x-y]%mod; }
int qpow(int a,int b,int res=1){
for(;b;b>>=1,a=a*a%mod)
if(b&1) res=res*a%mod;
return res;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=4000;i++) fac[i]=fac[i-1]*i%mod;
inv[4000]=qpow(fac[4000],mod-2);
for(int i=3999;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
} using namespace Combinatorics;
signed main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
n=read(); m=read(); init();
for(int i=1;i<n;i++){
sum=0;
for(int j=1;j<m;j++){
(sum+=C(i+j-1,i)*C(m-j+i-1,m-j))%=mod;
(ans+=sum*C(n-i+j-1,j)%mod*C(n-i+m-j-1,n-i))%=mod;
}
}
swap(n,m);
for(int i=1;i<n;i++){
sum=0;
for(int j=1;j<m;j++){
(ans+=sum*C(n-i+j-1,j)%mod*C(n-i+m-j-1,n-i))%=mod;
(sum+=C(i+j-1,i)*C(m-j+i-1,m-j))%=mod;
}
}
ans=ans*2%mod;
write(ans,'\n');
return 0;
}
T2 连通块
最长路径一定是由连通块直径端点贡献的,于是删边变加边,然后维护树的直径就好。
\(code:\)
T2
#include<bits/stdc++.h>
using namespace std;
namespace IO{
int read(){
int x=0,f=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char Ch[50];
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmax(int& x,int y){ x=x>y?x:y; }
void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;
const int NN=200010;
int n,m,idx,uu[NN],vv[NN],id[NN<<1],head[NN];
struct edge{ int to,nex; }e[NN<<1];
struct opt{ int op,x; }o[NN];
bool ban[NN];
vector<int>ans;
void add(int a,int b,int i){
e[++idx]=(edge){b,head[a]}; head[a]=idx; id[idx]=i;
e[++idx]=(edge){a,head[b]}; head[b]=idx; id[idx]=i;
}
namespace Tree_Chain{
int fa[NN],top[NN],siz[NN],son[NN],dep[NN];
void dfs1(int s,int f){
fa[s]=f; siz[s]=1; dep[s]=dep[f]+1;
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=f){
dfs1(v,s);
siz[s]+=siz[v];
if(siz[v]>siz[son[s]]) son[s]=v;
}
}
void dfs2(int s,int t){
top[s]=t;
if(!son[s]) return;
dfs2(son[s],t);
for(int i=head[s];i;i=e[i].nex)
if(e[i].to!=fa[s]&&e[i].to!=son[s]) dfs2(e[i].to,e[i].to);
}
int LCA(int x,int y){
while(top[x]!=top[y])
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
} using namespace Tree_Chain;
namespace DSU{
int ff[NN],du[NN],dv[NN],le[NN];
int getf(int x){ return ff[x]==x?x:ff[x]=getf(ff[x]); }
void merge(int x,int y){
x=getf(x); y=getf(y); ff[y]=x;
int ux=du[x],vx=dv[x],uy=du[y],vy=dv[y];
int dis1=dep[ux]+dep[uy]-2*dep[LCA(ux,uy)];
int dis2=dep[vx]+dep[uy]-2*dep[LCA(vx,uy)];
int dis3=dep[ux]+dep[vy]-2*dep[LCA(ux,vy)];
int dis4=dep[vx]+dep[vy]-2*dep[LCA(vx,vy)];
int dis5=le[x],dis6=le[y];
le[x]=max(max(dis1,dis2),max(max(dis3,dis4),max(dis5,dis6)));
if(le[x]==dis1) du[x]=ux,dv[x]=uy;
if(le[x]==dis2) du[x]=vx,dv[x]=uy;
if(le[x]==dis3) du[x]=ux,dv[x]=vy;
if(le[x]==dis4) du[x]=vx,dv[x]=vy;
if(le[x]==dis5) du[x]=ux,dv[x]=vx;
if(le[x]==dis6) du[x]=vy,dv[x]=uy;
}
} using namespace DSU;
signed main(){
freopen("block.in","r",stdin);
freopen("block.out","w",stdout);
n=read(); m=read(); idx=0;
for(int i=1;i<n;i++)
uu[i]=read(),vv[i]=read(),add(uu[i],vv[i],i);
for(int i=1;i<=n;i++)
ff[i]=du[i]=dv[i]=i;
dfs1(1,0); dfs2(1,1);
for(int i=1;i<=m;i++){
o[i].op=read(),o[i].x=read();
if(o[i].op==1) ban[o[i].x]=1;
}
for(int i=1;i<n;i++)
if(!ban[i]) merge(uu[i],vv[i]);
for(int i=m;i;i--)
if(o[i].op==1) merge(uu[o[i].x],vv[o[i].x]);
else{
int u=o[i].x,f=getf(u);
int res=max(dep[du[f]]+dep[u]-2*dep[LCA(du[f],u)],dep[dv[f]]+dep[u]-2*dep[LCA(dv[f],u)]);
ans.push_back(res);
}
for(int i=ans.size()-1;~i;i--) write(ans[i],'\n');
return 0;
}
T3 军队
扫描线我是乱搞的,写了个估价函数一样的东西,再加上标记永久化标记合并剪枝。
令 \(a_i\) 为第 \(i\) 行较少性别的个数,那么对于 \(a_i\geq \left\lfloor\frac{y}{2}\right\rfloor\) ,贡献为 \(\left\lfloor\frac{y}{2}\right\rfloor\times\left\lceil\frac{y}{2}\right\rceil\) ,否则为 \(a_i\times(y-a_i)\) 。可以将 \(a\) , \(y\) 排序,这样 \(\geq \left\lfloor\frac{y}{2}\right\rfloor\) 的数量是单调不降的,再维护两个前缀和就可以 \(O(1)\) 计算询问。
\(code:\)
T3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL;
int read(){
int x=0,f=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char Ch[50];
void write(LL x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
const int NN=300010;
int n,m,c,k,q;
LL male[NN];
struct mat{ int x1,y1,x2,y2; }o[NN];
namespace Segment_Tree{
#define ld rt<<1
#define rd (rt<<1)|1
int mx[NN<<2],sum[NN<<2],len[NN<<2];
vector<pair<int,int>>add[NN],del[NN];
void pushup(int rt){
int tmp=min(sum[ld],sum[rd]);
sum[ld]-=tmp; mx[ld]-=tmp;
sum[rd]-=tmp; mx[rd]-=tmp;
sum[rt]+=tmp;
mx[rt]=max(mx[ld],mx[rd])+sum[rt];
}
void build(int rt,int l,int r){
len[rt]=r-l+1;
if(l==r) return;
int mid=l+r>>1;
build(ld,l,mid);
build(rd,mid+1,r);
}
void modify(int opl,int opr,int typ,int rt=1,int l=1,int r=m){
if(l>=opl&&r<=opr){
sum[rt]+=typ; mx[rt]+=typ;
return;
}
int mid=l+r>>1;
if(opl<=mid) modify(opl,opr,typ,ld,l,mid);
if(opr>mid) modify(opl,opr,typ,rd,mid+1,r);
pushup(rt);
}
int query(int now,int rt=1,int l=1,int r=m){
now+=sum[rt];
if(now>=k) return len[rt];
if(l==r) return 0;
int mid=l+r>>1,res=0;
if(mx[ld]+now>=k) res+=query(now,ld,l,mid);
if(mx[rd]+now>=k) res+=query(now,rd,mid+1,r);
return res;
}
#undef ld
#undef rd
} using namespace Segment_Tree;
void prework(){
build(1,1,m);
for(int i=1;i<=c;i++){
add[o[i].x1].push_back(make_pair(o[i].y1,o[i].y2));
del[o[i].x2+1].push_back(make_pair(o[i].y1,o[i].y2));
}
for(int i=1;i<=n;i++){
for(auto v:add[i]) modify(v.first,v.second,1);
for(auto v:del[i]) modify(v.first,v.second,-1);
male[i]=query(0);
if(male[i]>m/2) male[i]=m-male[i];
}
}
struct Query{
int x,y,id;
bool operator<(const Query& a)const{
return y>a.y;
}
}qq[NN<<2];
LL pos,ans[NN<<2],pre1[NN],pre2[NN];
void solve(){
sort(qq+1,qq+q+1);
sort(male+1,male+n+1,[](int a,int b)->bool{return a>b;});
for(int i=1;i<=n;i++){
pre1[i]=pre1[i-1]+male[i];
pre2[i]=pre2[i-1]+male[i]*male[i];
}
for(int i=1;i<=q;i++){
LL y=qq[i].y/2,tmp=y*(qq[i].y-y);
while(male[pos+1]>=y&&pos<n) ++pos;
if(qq[i].x<=pos) ans[qq[i].id]=tmp*qq[i].x;
else ans[qq[i].id]=tmp*pos+qq[i].y*(pre1[qq[i].x]-pre1[pos])-pre2[qq[i].x]+pre2[pos];
}
for(int i=1;i<=q;i++) write(ans[i],'\n');
}
signed main(){
freopen("army.in","r",stdin);
freopen("army.out","w",stdout);
n=read(); m=read(); c=read(); k=read(); q=read();
for(int i=1;i<=c;i++)
o[i].x1=read(),o[i].y1=read(),o[i].x2=read(),o[i].y2=read();
for(int i=1;i<=q;i++)
qq[i].x=read(), qq[i].y=read(), qq[i].id=i;
prework(); solve();
return 0;
}
T4 棋盘
因为头尾一直在变化,所以可以在中间维护信息。

实现极其神仙。
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
int read(){
int x=0,f=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char Ch[50];
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
const int NN=25,MM=100010,mod=998244353;
int n,q,up,dn;
int t1,t2,s1[MM][NN][NN],s2[MM][NN][NN],s3[MM][NN][NN],s4[MM][NN][NN];
char ch[MM][NN],op[NN];
void pls(int& x,int y){ x+=y; if(x>=mod) x-=mod; }
bool can(int x){ return x>=0&&x<n&&op[x]=='.'; }
void insert_up(int mat[MM][NN][NN],int typ){
t1+=typ; memset(mat[t1],0,sizeof(mat[t1]));
if(t1==1){ for(int i=0;i<n;i++) mat[1][i][i]=can(i); return; }
if(t1>1)
for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(mat[t1-1][i][j]){
if(can(i-2)) pls(mat[t1][i-2][j],mat[t1-1][i][j]);
if(can(i+2)) pls(mat[t1][i+2][j],mat[t1-1][i][j]);
}
if(t1>2)
for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(mat[t1-2][i][j]){
if(can(i-1)) pls(mat[t1][i-1][j],mat[t1-2][i][j]);
if(can(i+1)) pls(mat[t1][i+1][j],mat[t1-2][i][j]);
}
}
void insert_dn(int mat[MM][NN][NN],int typ){
t2+=typ; memset(mat[t2],0,sizeof(mat[t2]));
if(t2==1){ for(int i=0;i<n;i++) mat[1][i][i]=can(i); return; }
if(t2>1)
for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(mat[t2-1][i][j]){
if(can(j-2)) pls(mat[t2][i][j-2],mat[t2-1][i][j]);
if(can(j+2)) pls(mat[t2][i][j+2],mat[t2-1][i][j]);
}
if(t2>2)
for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(mat[t2-2][i][j]){
if(can(j-1)) pls(mat[t2][i][j-1],mat[t2-2][i][j]);
if(can(j+1)) pls(mat[t2][i][j+1],mat[t2-2][i][j]);
}
}
void rebuild(){
t1=t2=0;
for(int i=dn;i>=up;i--){
memcpy(op,ch[i],sizeof(op));
if(!t1) insert_up(s1,1),insert_dn(s2,1);
else insert_up(s3,0),insert_up(s1,1);
}
}
void add(){
++dn; scanf("%s",ch[dn]);
memcpy(op,ch[dn],sizeof(op));
if(!t1) rebuild();
else insert_dn(s4,0),insert_dn(s2,1);
}
void del(){
++up; --t1;
if(!t1&&up<=dn) rebuild();
}
void ask(){
int x=read()-1,y=read()-1,res=0;
if(up>dn) return puts("0"),void();
for(int i=0;i<n;i++) pls(res,1ll*s1[t1][x][i]*s2[t2][i][y]%mod);
if(t1>1&&t2>1)
for(int i=0;i<n;i++){
if(i-1>=0) pls(res,1ll*s3[t1-1][x][i]*s4[t2-1][i-1][y]%mod);
if(i+1<n) pls(res,1ll*s3[t1-1][x][i]*s4[t2-1][i+1][y]%mod);
}
write(res,'\n');
}
signed main(){
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
n=read(); q=read(); up=1;
while(q--){
scanf("%s",op);
if(op[0]=='A') add();
if(op[0]=='D') del();
if(op[0]=='Q') ask();
}
return 0;
}

浙公网安备 33010602011771号