Loading

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

Page Views Count

6.15 冲刺国赛模拟 19

T1 矩阵

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

另一种做法根据修改不超过 \(10^4\) 次,对操作分块,每 \(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\) 层,每层选出的操作都不相交,先建出 \(S\to 1\to 2\to\cdots\to n\to n+1\to T\),除 \(S\to 1\) 有容量 \(a\) 的限制外其他容量设成最大,且费用均为 \(0\)

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

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

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

点击查看代码
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\) 的充要条件是存在向量线性相关,加上一个取模的条件同样成立。

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

\[\dfrac{v^{n\times n}-\prod_{i=1}^n (v^n-v^{i-1})}{v^{n\times 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

就是要求 \(sum-\max \equiv 0\pmod m\)\(sum-\max\neq 0\) 的区间个数,最值考虑线段树+单调栈或分治。线段树没法维护模意义下最小值和区间修改,所以只能分治。

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

点击查看代码
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

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

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

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

点击查看代码
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;
}
posted @ 2023-06-18 11:44  SoyTony  阅读(23)  评论(0编辑  收藏  举报