【考后总结】6 月西安 NOI 模拟赛 2

Page Views Count

6.15 冲刺国赛模拟 19

T1 矩阵#

直接二维树状数组就能过。

另一种做法根据修改不超过 104 次,对操作分块,每 B 次分块暴力处理一次,剩下部分每次询问都暴力算。

点击查看代码
int n,m,q;
struct BinaryIndexedTree{
#define lowbit(x) (x&-x)
    ll a[1005][1005];
    inline void update(int x,int y,ll k){
        for(int i=x;i<=n;i+=lowbit(i)){
            for(int j=y;j<=m;j+=lowbit(j)){
                a[i][j]+=k;
            }
        }
    }
    inline ll query(int x,int y){
        ll res=0;
        for(int i=x;i;i-=lowbit(i)){
            for(int j=y;j;j-=lowbit(j)){
                res+=a[i][j];
            }
        }
        return res;
    }
}B;

int main(){
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    n=read(),m=read(),q=read();
    while(q--){
        int opt=read();
        if(opt==1){
            int x=read(),y=read();
            ll k;
            scanf("%lld",&k);
            B.update(x,y,k);
        }
        else{
            int x=read(),y=read(),a=read(),b=read();
            if(x>a) swap(x,a);
            if(y>b) swap(y,b);
            ll ans=B.query(a,b)-B.query(a,y-1)-B.query(x-1,b)+B.query(x-1,y-1);
            printf("%lld\n",ans);
        }
    }
    return 0;
}

T2 种草#

考虑 a 全相等的特殊性质,观察到这可以看做分成 a 层,每层选出的操作都不相交,先建出 S12nn+1T,除 S1 有容量 a 的限制外其他容量设成最大,且费用均为 0

之后对于 (l,r,w),连 lr+1 的边,且容量量为 1,费用是 w,这样从 S 出发的 a 条流的每一条都只能从左向右,在最大费用流的限制下,就会尽量走 w 更大的部分。

考虑 a 不同怎么办,由于 a 单调不降,可以把刚才的 a 层改成每一层都有一个区间限制 [x,n],且 x 单调不降,这样对于每个 kan,找到第一个不小于 kai,由 Si 连一条容量为 1 的边,这样就相当于限制了这一层只能从 i 开始,然后就要跑费用流了。

SPFA 的 EK 费用流不太能过,要用 Primal-Dual 原始对偶算法,复杂度是 O(mlogma)

点击查看代码
int n,m;
int a[maxn];
struct Segment{
    int l,r,w;
    Segment()=default;
    Segment(int l_,int r_,int w_):l(l_),r(r_),w(w_){}
}Seg[maxn];
int S,T;
struct edge{
    int to,nxt,lim,c;
}e[maxn*4+40];
int head[maxn*2+40],cnt;
int deg[maxn];
inline void add_edge(int u,int v,int w,int c){
    e[++cnt].to=v,e[cnt].nxt=head[u],e[cnt].lim=w,e[cnt].c=c,head[u]=cnt;
    e[++cnt].to=u,e[cnt].nxt=head[v],e[cnt].lim=0,e[cnt].c=-c,head[v]=cnt;
    ++deg[v];
}
ll h[maxn];
inline void BFS(){
    queue<int> q;
    memset(h,0x3f,sizeof(h));
    q.push(S);
    h[S]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to,w=e[i].c;
            if(e[i].lim){
                h[v]=min(h[v],h[u]+w);
                --deg[v];
                if(!deg[v]) q.push(v);
            }
        }
    }
}
struct node{
    int u;
    ll d;
    node()=default;
    node(int u_,ll d_):u(u_),d(d_){}
    bool operator<(const node& rhs)const{
        return d>rhs.d;
    }  
};
ll dis[maxn];
bool vis[maxn];
int pre[maxn];
inline void Dijkstra(){
    priority_queue<node> q;
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    q.push(node(S,0));
    dis[S]=0;
    while(!q.empty()){
        int u=q.top().u;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            ll w=e[i].c+h[u]-h[v];
            if(e[i].lim&&dis[u]+w<dis[v]){
                dis[v]=dis[u]+w,pre[v]=i;
                q.push(node(v,dis[v]));
            }
        }
    }
}
inline ll mincost_maxflow(){
    BFS();
    ll mincost=0;
    while(1){
        Dijkstra();
        if(dis[T]==0x3f3f3f3f3f3f3f3f) return -mincost;
        for(int u=1;u<=T;++u) h[u]+=dis[u];
        int flow=inf;
        for(int u=T;u!=S;u=e[pre[u]^1].to){
            flow=min(flow,e[pre[u]].lim);
        }
        for(int u=T;u!=S;u=e[pre[u]^1].to){
            mincost+=1ll*e[pre[u]].c*flow;
            e[pre[u]].lim-=flow,e[pre[u]^1].lim+=flow;
        }
    }
}

int main(){
    freopen("weed.in","r",stdin);
    freopen("weed.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i){
        int l=read(),r=read(),w=read();
        Seg[i]=Segment(l,r,w);
    }
    S=n+2,T=n+3;
    cnt=1;
    for(int i=1;i<=n;++i) add_edge(i,i+1,inf,0);
    add_edge(n+1,T,inf,0);
    for(int i=1;i<=m;++i) add_edge(Seg[i].l,Seg[i].r+1,1,-Seg[i].w);
    for(int i=1,j=1;i<=n;++i){
        while(j<=a[i]){
            add_edge(S,i,1,0);
            ++j;
        }
    }
    printf("%lld\n",mincost_maxflow());
    return 0;
}

6.16 ACM 趣味赛

C The Longest Wall#

签到题,两个 LDS 拼起来即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int maxn=1e3+10;

inline int read(){
    int x=0,w=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x*w;
}

int n;
int a[maxn];
int f[maxn],g[maxn];

int main(){
    n=read();
    for(int i=1;i<=n;++i) a[i]=read();
    a[0]=0x3f3f3f3f,a[n+1]=0x3f3f3f3f;
    for(int i=1;i<=n;++i){
        for(int j=0;j<i;++j){
            if(a[i]<a[j]) f[i]=max(f[i],f[j]+1);
        }
    }
    for(int i=n;i>=1;--i){
        for(int j=n+1;j>i;--j){
            if(a[i]<a[j]) g[i]=max(g[i],g[j]+1);
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i) ans=max(ans,f[i]+g[i]-1);
    printf("%d\n",ans); 
    return 0;
}

D Singular Matrix#

行列式为 0 的充要条件是存在向量线性相关,加上一个取模的条件同样成立。

考虑统计线性无关的方阵个数,第一行是可以填除零向量外的 (vn1) 种,第二行不能填和第一行共线的 v 种,所以可以填 (vnv2) 种。根据向量基本定理,第三行不能填由前两行可以表示出的 v2 种,以此类推。最后算出来答案就是:

vn×ni=1n(vnvi1)vn×n

点击查看代码
inline int q_pow(int A,int B,int P){
    int res=1;
    while(B){
        if(B&1) res=1ll*res*A%P;
        A=1ll*A*A%P;
        B>>=1;
    }
    return res;
}

int n,v,mod;
int ans;
int main(){
    n=read(),v=read(),mod=read();
    ans=q_pow(q_pow(v,1ll*n*n%(mod-1),mod),mod-2,mod);
    int pwn=q_pow(v,n,mod),pw=1;
    for(int i=1;i<=n;++i){
        ans=1ll*ans*(pwn-pw+mod)%mod;
        pw=1ll*pw*v%mod;
    }
    ans=(1-ans+mod)%mod;
    printf("%d\n",ans);
    return 0;
}

K Distribution#

就是要求 summax0(modm)summax0 的区间个数,最值考虑线段树+单调栈或分治。线段树没法维护模意义下最小值和区间修改,所以只能分治。

分治很好做,只有一个最大值的限制,开桶计算答案就行了。

点击查看代码
int n,m;
int a[maxn];
int mx[maxn],sum[maxn];
int tot[maxw];
ll ans;
void solve(int l,int r){
    if(l==r) return;
    int mid=(l+r)>>1;
    solve(l,mid),solve(mid+1,r);
    mx[mid]=a[mid],sum[mid]=a[mid]%m,mx[mid+1]=a[mid+1],sum[mid+1]=a[mid+1]%m;
    for(int i=mid-1;i>=l;--i) mx[i]=max(mx[i+1],a[i]),sum[i]=(sum[i+1]+a[i])%m;
    for(int i=mid+2;i<=r;++i) mx[i]=max(mx[i-1],a[i]),sum[i]=(sum[i-1]+a[i])%m;
    for(int i=mid,j=mid;i>=l;--i){
        while(j<r&&mx[j+1]<=mx[i]){
            ++j;
            ++tot[sum[j]];
        } 
        ans+=tot[(mx[i]-sum[i]+m)%m];
    }
    for(int i=mid+1;i<=r;++i) tot[sum[i]]=0;
    for(int i=mid+1,j=mid+1;i<=r;++i){
        while(j>l&&mx[j-1]<mx[i]){
            --j;
            ++tot[sum[j]];
        }
        ans+=tot[(mx[i]-sum[i]+m)%m];
    }
    for(int i=mid;i>=l;--i) tot[sum[i]]=0;
}

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    solve(1,n);
    printf("%lld\n",ans);
    return 0;
}

H Mass Troops#

原题:Luogu-P3574 POI 2014 FAR-FarmCraft

fu 激活 u 子树全部节点所用最短时间,那么合并子树就是找一个顺序分别走各个子树。具体形式应当是:max(2siz+1+fv),即完成 v 子树的时间以及走完前面子树并走到 v 的时间之和,我们希望最小化最大值。

一个经典套路是贪心的顺序考虑交换,即假设 x,y 两子树在枚举顺序中相邻,考虑交换顺序会有什么影响。

发现前面枚举的子树对答案的贡献是一样的,因此差别只在 x,y 之间,具体是比较 max(fx,2sizx+1+fy)max(fy,2sizy+1+fx)。由于 fx<2sizy+1+fx,fy<2sizx+1+fy,所以实际的大小关系只是由 2sizx+1+fy2sizy+1+fx 决定的,移项之后按照 2sizxfx 升序排序即可。

点击查看代码
int n;
int a[maxn];
vector<int> E[maxn];
int fa[maxn],siz[maxn],dfn[maxn],dfncnt;
void dfs1(int u,int f){
    fa[u]=f,siz[u]=1,dfn[u]=++dfncnt;
    for(int v:E[u]){
        if(v==f) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
    }
}
int dp[maxn];
void dfs2(int u){
    for(int v:E[u]){
        dfs2(v);
    }
    sort(E[u].begin(),E[u].end(),[&](int x,int y){
        return 2*siz[x]-dp[x]<2*siz[y]-dp[y];
    });
    int sum=0;
    for(int v:E[u]){
        dp[u]=max(dp[u],sum+1+dp[v]);
        sum+=2*siz[v];
    }
    if(u==1) dp[u]=max(dp[u],2*n-2+a[1]);
    else dp[u]=max(dp[u],a[u]);
}

int main(){
    n=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        E[u].push_back(v);
        E[v].push_back(u);
    }
    dfs1(1,0);
    for(int i=2;i<=n;++i){
        sort(E[i].begin(),E[i].end(),[&](int x,int y){
            return dfn[x]>dfn[y];
        });
        E[i].pop_back();
    }
    dfs2(1);
    printf("%d\n",dp[1]);
    return 0;
}

作者:SoyTony

出处:https://www.cnblogs.com/SoyTony/p/Simulation_Problems_of_NOI_in_Xian_June_2.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   SoyTony  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示