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 棋盘

因为头尾一直在变化,所以可以在中间维护信息。
image

实现极其神仙。

\(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;
}
posted @ 2021-11-16 21:30  keen_z  阅读(67)  评论(0)    收藏  举报