CF1292 div.1 做题记录

发布时间 2023-05-29 20:32:33作者: luo_shen

A

CF题面

若某一列的第 \(i\) 行禁止移动,那么看另一列的第 \(i-1,i,i+1\) 行是否存在禁止移动的格子,若存在为 No,否则为 Yes,这个只需要在改变一个点状态时判断即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e5+10;
int n,m,f[2][N],s;
void solve(){
    read(n),read(m);
    for(int i=1;i<=m;i++){
        int a,b;
        read(a),read(b);
        a--;
        f[a][b]^=1;
        if(f[a][b]){
            if(f[a^1][b+1]){
                s++;
            }
            if(f[a^1][b]){
                s++;
            }
            if(f[a^1][b-1]){
                s++;
            }
        }
        else{
            if(f[a^1][b+1]){
                s--;
            }
            if(f[a^1][b]){
                s--;
            }
            if(f[a^1][b-1]){
                s--;
            }
        }
        if(s){
            puts("No");
        }
        else{
            puts("Yes");
        }
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

B

CF题面

可以发现有可能到达的点不超过 \(60\) 个,将其排好序。显然的是若取到点 \(l\) 和点 \(r\),那么必然取得到区间 \([l,r]\) 内的所有点,因为是曼哈顿距离,点 \(l\) 到点 \(r\) 的最短路径中必然有经过其它点的路径,直接区间dp,求出从起始点开始遍历所有点的最短距离是多少。

最后找到区间长度最长且满足最短距离小于 \(t\)\([l,r]\) 的区间长度。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int __int128
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=100,MX=2e16;
int x[N],y[N],ax,ay,bx,by,stx,sty,t,n,f[N][N][2];
int Abs(int x){
    if(x<0){
        return -x;
    }
    return x;
}
void solve(){
    read(x[0]),read(y[0]),read(ax),read(ay),read(bx),read(by),read(stx),read(sty),read(t);
    while(1){
        n++;
        x[n]=x[n-1]*ax+bx;
        y[n]=y[n-1]*ay+by;
        if(x[n]*ax>MX||y[n]*ay>MX){
            break;
        }
    }
    memset(f,0x3f,sizeof(f));
    for(int i=0;i<=n;i++){
        f[i][i][0]=f[i][i][1]=Abs(stx-x[i])+Abs(sty-y[i]);
    }
    for(int len=2;len<=n+1;len++){
        for(int l=0;l<=n-len+1;l++){
            int r=l+len-1;
            f[l][r][0]=min(f[l][r][0],min(f[l+1][r][0]+Abs(x[l+1]-x[l])+Abs(y[l+1]-y[l]),f[l+1][r][1]+Abs(x[r]-x[l])+Abs(y[r]-y[l])));
            f[l][r][1]=min(f[l][r][1],min(f[l][r-1][0]+Abs(x[r]-x[l])+Abs(y[r]-y[l]),f[l][r-1][1]+Abs(x[r]-x[r-1])+Abs(y[r]-y[r-1])));
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++){
        for(int j=i;j<=n;j++){
            if(f[i][j][0]<=t||f[i][j][1]<=t){
                ans=max(ans,j-i+1);
            }
        }
    }
    write_endl(ans);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

C

CF题面

题面要求的是:

\[\sum\limits_{1\le s\le t\le n}\operatorname{mex}S_{(s,t)} \]

其中 \(\operatorname{mex}\) 表示集合中没有出现过的最小自然数(从 \(0\) 开始),\(S_{(s,t)}\) 表示路径 \((s,t)\) 上所有边的边权组成的集合。

对于这样的一个形式,有一个非常常见的变形,将其转化为:

\[\sum\limits_{x=1}^n\sum\limits_{\operatorname{mex}S_{(s,t)}=x}x \]

也就是

\[\sum\limits_{x=1}^n\sum\limits_{\operatorname{mex}S_{(s,t)}\ge x}1 \]

可以发现这就相当于给一条链赋值,求经过其中的 \([0,x]\) 这一段的路径条数并对所有 \(x\) 求和。

假定我们已经知道是对哪条链赋值,\([0,x]\) 肯定位于一段连续的路径上。若不位于一段连续路径上,这段路径中多了一个 \(y(y>x)\),那么调整一下,将 \(y\) 放到路径端点。经过 \([0,x]\) 的路径数不会减少,经过 \([0,y]\) 的路径数不会增多,显然这是不劣的。

那么这显然变成了一个区间dp问题,可以发现这题支持 \(O(n^2)\) 暴力dp。令 \(f_{u,v}\) 表示对 \((u,v)\) 这条链赋值后答案的最大值,\(fa_{u,v}\) 表示以 \(u\) 为根是 \(v\) 的父亲,\(siz_{u,v}\) 表示 \(u\) 为根时 \(v\) 的子树大小。\(f_{u,v}=\max(f_{u,fa_{u,v}},f_{fa_{v,u},v})+siz_{u,v}\times siz_{v,u}\),最后对于所有 \(f_{u,v}\)\(\max\) 即为答案。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=3010;
int n,siz[N][N],fa[N][N];
ll f[N][N];
vector<int>e[N];
void dfs(int u,int father,int rt){
    fa[rt][u]=father;
    siz[rt][u]=1;
    for(auto v:e[u]){
        if(v==father){
            continue;
        }
        dfs(v,u,rt);
        siz[rt][u]+=siz[rt][v];
    }
}
ll work(int u,int v){
    if(u==v){
        return 0;
    }
    if(f[u][v]){
        return f[u][v];
    }
    f[u][v]=max(work(fa[v][u],v)+1ll*siz[u][v]*siz[v][u],work(u,fa[u][v])+1ll*siz[v][u]*siz[u][v]);
    return f[u][v];
}
void solve(){
    read(n);
    for(int i=1,u,v;i<n;i++){
        read(u),read(v);
        e[u].pb(v);
        e[v].pb(u);
    }
    for(int i=1;i<=n;i++){
        dfs(i,0,i);
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            ans=max(ans,work(i,j));
        }
    }
    write_endl(ans);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

D

CF题面

可以发现这是一棵无限树,显然不可以在原树上做这个问题。可以发现本质的 \(k\) 最多只有 \(3000\) 个,可以尝试建出虚树。这棵树和阶乘的性质都比较有趣,若我们已经知道包含 \((i-1)!\) 的虚树,现在加入 \(i!\) 这个点。这两个点的差距只是多乘了一个 \(i\)。若 \(i\) 为质数,那么这两个点的 lca 一定为 \(1\) 号点。若 \(i\) 不为质数,那么原树上两点的 lca 肯定是从 \(1\) 号点开始一直走 \((i-1)!\) 的最大质因子的边,直到走完所有大于等于 \(i\) 的最大质因子的边。可以用树状数组维护每个质因子的出现次数,那么每个点和它们的 lca 的深度均可以算出来,虚树上任意相邻两点间边的长度也可以算出来。最后答案即为这棵虚树的带权重心。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
            int f=1;
            char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=2e4+10,inf=1e18;
int n,val[N],prime[N],min_prime[N],min_id[N],cnt;
void init(int mx){
    for(int i=2;i<=mx;i++){
        if(!min_prime[i]){
            prime[++cnt]=i;
            min_prime[i]=i;
            min_id[i]=cnt;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=mx;j++){
            min_prime[i*prime[j]]=prime[j];
            min_id[i*prime[j]]=j;
            if(i%prime[j]==0){
                break;
            }
        }
    }
}
namespace Fenwick_Tree{
    int c[N];
    int lowbit(int x){
        return x&(-x);
    }
    void update(int x,int val){
        while(x){
            c[x]+=val;
            x-=lowbit(x);
        }
    }
    int query(int x){
        int res=0;
        while(x<=cnt){
            res+=c[x];
            x+=lowbit(x);
        }
        return res;
    }
}
int fa[N],len[N],tot=n;
void build(){
    tot=5000;
    int Sum=0;
    for(int i=2;i<=5000;i++){
        if(min_prime[i]==i){
            fa[i]=1;
            Sum++;
            len[i]=Sum;
            Fenwick_Tree::update(min_id[i],1);
            continue;
        }
        int mx=0,Val=i,Cnt=0;
        while(min_prime[Val]){
            mx=max(mx,min_id[Val]);
            Val/=min_prime[Val];
            Cnt++;
        }
        int same=Fenwick_Tree::query(mx),dis=Sum-same,x=i-1;
        while(dis>=len[x]){
            dis-=len[x];
            x=fa[x];
        }
        if(dis==0){
            fa[i]=x;
            Sum+=Cnt;
            len[i]=Sum-same;
        }
        else{
            int p=++tot;
            fa[p]=fa[x];
            len[p]=len[x]-dis;
            fa[x]=p;
            len[x]=dis;
            Sum+=Cnt;
            fa[i]=p;
            len[i]=Sum-same;
        }
        Val=i;
        while(min_prime[Val]){
            Fenwick_Tree::update(min_id[Val],1);
            Val/=min_prime[Val];
        }
    }
}
int siz[N],ans[N],mn=inf;
vector<int>e[N];
void dfs(int u,int fa,int dis){
    siz[u]=val[u];
    for(auto v:e[u]){
        if(v==fa){
            continue;
        }
        dfs(v,u,dis+len[v]);
        siz[u]+=siz[v];
        ans[1]+=siz[v]*len[v];
    }
}
void get_rt(int u,int fa){
    if(ans[u]<mn){
        mn=ans[u];
    }
    for(auto v:e[u]){
        if(v==fa){
            continue;
        }
        ans[v]=ans[u]+len[v]*(n-siz[v]*2);
        get_rt(v,u);
    }
}
void solve(){
    init(5000);
    read(n);
    for(int i=1,x;i<=n;i++){
        read(x);
        val[x]++;
    }
    val[1]+=val[0];
    build();
    for(int i=2;i<=tot;i++){
        e[fa[i]].pb(i);
        e[i].pb(fa[i]);
    }
    dfs(1,0,0);
    get_rt(1,0);
    write_endl(mn);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}