NOIP模拟<反思>(33~35)

NOIP2023模拟12联测33#

构造#

手摸你就会发现 ryxyryxyr,这样会更优,而且从第三行开始会有多余的贡献。

点击查看代码
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
using namespace std;
char s[100];
char ans[100][100];
int main(){
    freopen("ryx.in","r",stdin);
    freopen("ryx.out","w",stdout);
    int n;
    scanf("%d",&n);
    if(n<=20){
        if(n==0){
            cout<<1<<" "<<3<<endl;
            for(int i=1;i<=3;i++){
                cout<<"x";
            }
            return 0;
        }
        int x=n*2,y=3;
        printf("%d %d\n",x,y);
        for(int i=1;i<=n*2;i++){
            if(i%2) printf("ryx\n");
            else printf("xxx\n");
        }
        return 0;
    }
    s[1]='r';
    int tot=1;
    for(int i=1;i<=9;i++){
        s[++tot]='y';s[++tot]='x';
        s[++tot]='y';s[++tot]='r';
    }
    s[++tot]='y';s[++tot]='x';
    if(n<=100){
        int x=40,y=39;
        printf("%d %d\n",x,y);
        for(int i=1;i<=x;i++){
            for(int j=1;j<=y;j++){
                ans[i][j]='x';
            }
        }
        int sum=n;
        for(int i=1;i<=x;i++){
            if(sum<=19){
                i++;
                for(int j=1;j<=tot;j++){
                    if(sum){
                        ans[i][j]=s[j];
                        if(j>=3){
                            if(s[j]!=s[j-1] && s[j-1]!=s[j-2] && s[j]!=s[j-2]) sum--;
                        }    
                    }
                }
                break;
            }
            if(!sum){
                continue;
            }
            if(i%2){
                for(int j=1;j<=tot;j++) ans[i][j]=s[j];
                sum-=19;
            }
            else{
                continue;
            }
        }
        for(int i=1;i<=x;i++){
            for(int j=1;j<=y;j++){
                cout<<ans[i][j];
            }
            cout<<endl;
        }
        return 0;
    }
    int sum=n;
    int x=40,y=40;
    cout<<x<<" "<<y<<endl;
    for(int i=1;i<=x;i++){
        for(int j=1;j<=y;j++){
            ans[i][j]='y';
        }
    }
    for(int i=1;i<=x;i++){
        if(i==1 || i==2){
            for(int j=1;j<=tot;j++) ans[i][j]=s[j]; 
            sum-=19;
            continue;
        }
        if(sum>=(19+38)){
            for(int j=1;j<=tot;j++) ans[i][j]=s[j];
            sum-=(19+38);
            continue;
        }
        else{
            if(sum){
                sum--;
                ans[i][1]=s[1];
            }
            for(int j=2;j<=tot;j++){
                if(j%2==0) ans[i][j]='y';
                else{
                    if(sum>=3){
                        sum-=3;
                        ans[i][j]=s[j];
                    }
                    else{
                        break;
                    }
                }
            }
            break;
        }
    }
    if(sum){
        ans[1][y]=s[1],ans[2][y]=s[2];
        for(int i=3;i<=x;i++){
            if(!sum) break;
            ans[i][y]=s[i];
            if(sum){
                if(s[i]!=s[i-1] && s[i]!=s[i-2] && s[i-1]!=s[i-2]){
                    sum--;
                }
            }    
        }
    }
    for(int i=1;i<=x;i++){
        for(int j=1;j<=y;j++){
            cout<<ans[i][j];
        }
        cout<<endl;
    }
}

游戏#

手摸一场了考试样例没摸出来。不太擅长策略题。

从大到小排完序后,同学最优肯定是将概率分配给一个前缀,老师也是,我们可以二分一个 mid 表示学生能否使得被抓人数 mid
此时对于小于 midai 不需要考虑,对于大于等于的有 (1p)ai 的期望抓到人数,让 (1p)aimid 就可以求出下界,将下界加起来,如果大于 1 ,那就不合法,反之成立。

还可以考虑因为选的是一个前缀,且概率和为 1,m 为选的个数,所以

(1pi)×aians

piaiansai

i=1i=maiansai=1

化简的到 ans=m1i=1i=m1ai

对每个前缀求出 ansmin 就可以。

数数#

考虑在序列上一个一个加数,使其构成单调递减的序列 F,合法,考虑可以转移到的状态。

首先一开始是一个长度为 n 的空序列,然后你从小到大加入每一个数,有一些限制,假设你加入的数在 F 中出现过,且出现区间为 L,R。那么考虑将这个数 x 加入到一个空序列上,限制肯定是这个序列长度 len=R,小于 R 的话,个数不满足,大于 k 的话,F值就会大于 x。然后这个空序列会变成两个序列,然后考虑剩下的空序列最长 len==L1,因为跟上面一样,考虑只存长度,这样状态数只有不到 2.4×105 个,然后转移。

点击查看代码
#include<bits/stdc++.h>
#define int long long
const int mod=998244353;
using namespace std;
const int N=100;
int f[N],n;
int l[N],r[N];
map<vector<int>,int>mp;
queue<vector<int>> q;
vector<int> s,b;
map<vector<int>,bool> vis;
signed main(){
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) l[i]=n+1,r[i]=0;
    for(int i=1;i<=n;i++){
        scanf("%lld",&f[i]);
        l[f[i]]=min(l[f[i]],i),r[f[i]]=max(r[f[i]],i);
    }
    s.push_back(n);
    mp[s]=1;
    q.push(s);
    vis[s]=1;
    while(!q.empty()){
        s=q.front();
        q.pop();
        vis[s]=0;
        int siz=s.size();
        int i=siz;
        int len=0;
        for(int j=0;j<siz;j++) len=max(len,s[j]);
        int sum=0;
        int cnt=0;
        for(int j=0;j<siz;j++){
            if(s[j]==len) sum++;
            if(s[j]==r[i]) cnt++;
        }
        if(l[i]==n+1 || r[i]==0){
            for(int j=0;j<siz;j++){
                if((s[j]<len || sum>1) && s[j]>0){
                    for(int k=1;k<=s[j];k++){
                        b.clear();
                        b=s;
                        b.erase(b.begin()+j);
                        b.push_back(k-1);
                        b.push_back(s[j]-k);
                        sort(b.begin(),b.end());
                        if(!vis[b]){
                            vis[b]=1;
                            q.push(b);
                        }
                        mp[b]=(mp[b]+mp[s])%mod;
                    }    
                }
            }
        }
        else{
            if(len!=r[i]) continue;
            for(int j=0;j<siz;j++){
                for(int k=1;k<=s[j];k++){
                    b.clear();
                    b=s;
                    b.erase(b.begin()+j);
                    b.push_back(k-1);
                    b.push_back(s[j]-k);
                    sort(b.begin(),b.end());
                    if(b[b.size()-1]==l[i]-1){
                        if(!vis[b]){
                            vis[b]=1;
                            q.push(b);
                        }
                        mp[b]=(mp[b]+mp[s])%mod;    
                    }
                }    
            }
        } 
    }
    vector<int> ed;
    ed.clear();
    for(int i=1;i<=n+1;i++){
        ed.push_back(0);
    }
    printf("%lld",mp[ed]);
}

滈葕#

首先考虑 D,C,因为这个限制少,所以尽可能选会更优,选之前判一下哪些点不可以选 DC。然后选 A,B,这个可以染色,如果边权为 1 则颜色不同,否则相同,然后判无解。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5*1e5+10;
const int M=1e5+10;
int head[M],ver[N*2],nex[N*2],edge[N*2],tot=0;
void add(int x,int y,int w){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot,edge[tot]=w;
}
int flatd[M],flatc[M];
struct asd{
    int x,y,w;
}a[N];
int ans[M],getans=0;
bool vis[N],flat[N];
int dfs1(int x,int f){
    flat[x]=1;
    if(flatc[x]) return flatc[x];
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==f) continue;
        if(flat[y]){
            if(flatc[y]==1){
                flatc[x]=1;
                return 1;
            }
            continue;
        }
        if(dfs1(y,x)==1){
            flatc[x]=1;
            return 1;
        }
    }
    flatc[x]=2;
    return 0;
}
int dfs2(int x,int f){
    flat[x]=1;
    if(flatd[x]) return flatd[x];
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==f) continue;
        if(flat[y]){
            if(flatd[y]==1){
                flatd[x]=1;
                return 1;
            }
            continue;
        }
        if(dfs2(y,x)==1){
            flatd[x]=1;
            return 1;
        }
    }
    flatd[x]=2;
    return 0;
}
void dfs(int x,int f){
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==f) continue;
        if(ans[y]){
            if(edge[i]) if(ans[x]==ans[y]) getans=1;
            if(!edge[i]) if(ans[x]!=ans[y]) getans=1;
            continue;
        }
        if(edge[i]==1){
            if(ans[x]==1) ans[y]=2;
            else ans[y]=1;    
        }
        else{
            ans[y]=ans[x];
        }
        dfs(y,x);
    }
}
int main(){
    freopen("dopetobly.in","r",stdin);
    freopen("dopetobly.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
        if(a[i].w){
            flatd[a[i].x]=1;
            flatc[a[i].y]=1;
        }
    }
    for(int i=1;i<=m;i++){
        if(a[i].w==0){
            vis[a[i].x]=vis[a[i].y]=1;
            add(a[i].x,a[i].y,a[i].w);
        }
    }
    for(int i=1;i<=n;i++){
        if(!flatc[i]){
            dfs1(i,0);
        }
    }
    tot=0;
    memset(head,0,sizeof(head));
    memset(flat,0,sizeof(flat));
    for(int i=1;i<=m;i++){
        if(a[i].w==0){
            add(a[i].y,a[i].x,a[i].w);
        }
    }
    for(int i=1;i<=n;i++){
        if(!flatd[i]){
            dfs2(i,0);
        }
    }
    for(int i=1;i<=n;i++){
        if(flatd[i]==2) ans[i]=4;
        if(flatc[i]==2) ans[i]=3;
    }
    tot=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++){
        if(!ans[a[i].x] && !ans[a[i].y]){
            add(a[i].x,a[i].y,a[i].w);
            add(a[i].y,a[i].x,a[i].w);
        }
    }
    for(int i=1;i<=n;i++){
        if(!ans[i]){
            ans[i]=2;
            dfs(i,0);
        }
    }
    if(getans){
        printf("NO");
        return 0;
    }
    if(!getans) printf("YES\n");
    for(int i=1;i<=n;i++){
        if(ans[i]==1) printf("A");
        if(ans[i]==2) printf("B");
        if(ans[i]==3) printf("C");
        if(ans[i]==4) printf("D");
    }
}

NOIP2023模拟13联测34#

origen#

首先对 ai 做前缀异或和,记为 si

然后原式变为 i=0nj=i+1n(sisj)2

然后考虑第 i 位加入 si,之后的答案为 ans2,然后 ans2=(20+21++225)2,也就是将其二进制拆分,然后按位考虑,肯定有两位异或起来都为 1 的才会产生贡献,贡献类似于 2×a×b×kiki 表示贡献的次数,然后对于两位相等的没有系数 2

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=2*1e5+10;
int a[N],s[N];
int f[25][3][25][3];
int ans[N];
signed main(){
    freopen("origen.in","r",stdin);
    freopen("origen.out","w",stdout);
    int n;
    while(scanf("%lld",&n)==1){
        // scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        int cnt=0;
        for(int i=1;i<=n;i++){
            s[i]=s[i-1]^a[i];
            for(int j=1;j<=20;j++){
                for(int k=j;k<=20;k++){
                    int t1=((s[i]&(1ll<<(j-1)))>0);
                    int t2=((s[i]&(1ll<<(k-1)))>0);
                    f[j][t1][k][t2]++;
                    if(j==k){
                        ans[i]+=(1ll<<(j-1))*(1ll<<(k-1))%mod*f[j][t1^1][k][t2^1]%mod;
                        ans[i]%=mod;
                        continue;
                    }
                    ans[i]+=2*(1ll<<(j-1))*(1ll<<(k-1))%mod*f[j][t1^1][k][t2^1]%mod;
                    ans[i]%=mod;
                }
            }
            ans[i]+=s[i]*s[i]%mod;
            ans[i]%=mod;
            cnt=(cnt+ans[i])%mod;
        }
        printf("%lld",cnt);
    }
    
}

competition#

一开始写的线段树卡不过去,最后还是写的 set

将每个人做的题全离线到题的序列上,对题进行扫描线,考虑这道题会被做多少次,其实就是 。这时没加入一个人相当于加入一个断点,相反为删去一个断点,可以用 set 维护。

点击查看代码
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=2*1e6+10;
const int inv2=500000004;
struct asd{
    int l,r,op,x;
}a[N];
struct qwe{
    int op,x;
    friend bool operator <(qwe a,qwe b){
        return a.x<b.x;
    } 
};
vector<qwe> s[N];
set<int> q;
inline int mgml(int x,int p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
int st[N],top=0;
constexpr auto SIZE (1 << 26);
char in[SIZE], *p1 = in;
inline int rnt () {
    int x = 0; char c = *p1++;
    while (!isdigit (c)) c = *p1++;
    while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = *p1++;
    return x;
}
signed main(){
    freopen("competition.in","r",stdin);
    freopen("competition.out","w",stdout);
    fread (in, 1, SIZE, stdin);
    int n,m;
    n = rnt (), m = rnt ();
    for(int i=1;i<=n;++i){
        a[i].l = rnt (), a[i].r = rnt ();
        st[++top]=a[i].l,st[++top]=a[i].r+1;
    }
    st[++top]=m;
    sort(st+1,st+top+1);
    top=unique(st+1,st+top+1)-(st+1);
    for(int i=1;i<=n;i++){
        int ls=lower_bound(st+1,st+top+1,a[i].l)-st;
        int rs=lower_bound(st+1,st+top+1,a[i].r+1)-st;
        qwe b;
        b.op=1,b.x=i;
        s[ls].push_back(b);
        b.op=0,b.x=i;
        s[rs].push_back(b);
    }
    q.insert(0),q.insert(n+1);
    int sum=n*(n+1)/2;
    int ans=0;
    for(int i=1;i<=top;++i){
        for(int j=0;j<s[i].size();++j){
            qwe x=s[i][j];
            if(x.op==1){
                auto x1=q.lower_bound(x.x);
                auto x2=q.upper_bound(x.x);
                x1--;
                int len=*x2-*x1-1;
                int len1=x.x-*x1-1;
                int len2=*x2-x.x-1;
                sum=(sum-(len+1)*len/2%mod+(len1+1)*len1/2%mod+(len2+1)*len2/2%mod+mod)%mod;
                q.insert(x.x);
            }
            else{
                q.erase(x.x);
                auto x1=q.lower_bound(x.x);
                auto x2=q.upper_bound(x.x);
                x1--;
                int len=*x2-*x1-1;
                int len1=x.x-*x1-1;
                int len2=*x2-x.x-1;
                sum=(sum+(len+1)*len/2%mod-(len1+1)*len1/2%mod-(len2+1)*len2/2%mod)%mod;
            }
        }
        int cnt=(n*(n+1)/2%mod-sum+mod)%mod;
        ans=(ans+(n*(n+1)/2%mod-sum+mod)%mod)%mod;
        if(i!=top){
            ans=(ans+cnt*((st[i+1]-st[i]-1)%mod)%mod)%mod;
        }
    }
    int cnt=ans*mgml(n*(n+1)/2%mod,mod-2)%mod;
    printf("%lld",cnt);

}

tour#

disx 为根到 x 的简单路径上的点权和,分 xlca(x,y)lca(x,y)y 两部分讨论。

第一部分上 i 满足条件需要满足 disxdisidisidisfai,处理一下为 disx2×disidisfai;第二部分上的 i 要满足 disxdisfalca(x,y+disfaidislca(x,y)disidisfai,还是处理一下为 disxdisfalca(x,ydislca(x,y)disi2×disfai,然后发现 左侧只与 x,y 有关,右侧至于 i,相关。离线的话,可以维护两棵主席树,分别维护当前点到根的路径上 disi2×disfai2×disidisfai 的个数,然后查询的时候直接差分求。

考虑在线,就跟 森林 一样,直接暴力重建较小的子树。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int inf=1e9;
int head[N*2],ver[N*2],nex[N*2],tot=0;
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int f[N][50];
int a[N];
int fa[N],size[N],d[N];
int t;
struct TREE{
    struct tree{
        int l,r,sum;
    }tr[N*256];
    int rt[N],idx=0;
    void change(int &p,int q,int l,int r,int wh){
        p=++idx;
        tr[p]=tr[q];
        if(l==r){ 
            tr[p].sum++;
            return;
        }
        int mid=(l+r)>>1;
        if(wh<=mid) change(tr[p].l,tr[q].l,l,mid,wh);
        else change(tr[p].r,tr[q].r,mid+1,r,wh);
        tr[p].sum=tr[tr[p].l].sum+tr[tr[p].r].sum;
    }
    int ask(int p,int q,int l,int r,int ls,int rs){
        if(l>=ls && r<=rs) return tr[p].sum-tr[q].sum;
        int mid=(l+r)>>1;
        int ans=0;
        if(ls<=mid) ans+=ask(tr[p].l,tr[q].l,l,mid,ls,rs);
        if(rs>mid) ans+=ask(tr[p].r,tr[q].r,mid+1,r,ls,rs);
        return ans;
    }
}T[2];
int get_fa(int x){
    if(fa[x]==x) return x;
    else return get_fa(fa[x]);
}
int dis[N];
void merge(int x,int fat){
    dis[x]=dis[fat]+a[x];
    f[x][0]=fat;
    d[x]=d[fat]+1;
    for(int j=1;j<=t;j++){
        f[x][j]=f[f[x][j-1]][j-1];
    }
    T[0].change(T[0].rt[x],T[0].rt[fat],-inf,inf,2*dis[x]-dis[f[x][0]]);
    T[1].change(T[1].rt[x],T[1].rt[fat],-inf,inf,dis[x]-2*dis[f[x][0]]);
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fat) continue;
        merge(y,x);
    }
}
int ask_lca(int x,int y){
    if(d[x]>d[y]) swap(x,y);
    for(int i=t;i>=0;i--){
        if(d[f[y][i]]>=d[x]) y=f[y][i];
    }
    if(x==y) return x;
    for(int i=t;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            x=f[x][i],y=f[y][i];
        }
    }
    return f[x][0];
}
int main(){
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    int testmode;
    scanf("%d",&testmode);
    int n,q;
    scanf("%d%d",&n,&q);
    T[0].tr[0].sum=T[1].tr[0].sum=0;
    t=log2(n)+1;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        fa[i]=i;
        size[i]=1;
        d[i]=1;
        dis[i]=a[i];
        T[0].change(T[0].rt[i],T[0].rt[0],-inf,inf,2*a[i]);
        T[1].change(T[1].rt[i],T[1].rt[0],-inf,inf,a[i]);
    }
    int las=0;
    for(int i=1;i<=q;i++){
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(testmode==1){
            x=(x^las),y=y^las;
        }
        if(!op){
            add(x,y),add(y,x);
            int fx=get_fa(x),fy=get_fa(y);
            if(size[fx]<size[fy]){
                size[fy]+=size[fx];
                fa[fx]=fy;
                merge(x,y);
            }
            else{
                size[fx]+=size[fy];
                fa[fy]=fx;
                merge(y,x);
            }
        }
        else{
            int ans=0;
            int lca=ask_lca(x,y);
            ans+=T[0].ask(T[0].rt[x],T[0].rt[f[lca][0]],-inf,inf,-inf,dis[x]);
            ans+=T[1].ask(T[1].rt[y],T[1].rt[lca],-inf,inf,-inf,dis[x]-dis[f[lca][0]]-dis[lca]);
            printf("%d\n",ans);
            las=ans;
        }
    }
}

NOIP2023模拟14联测35#

charlotte#

dpu 表示子树中能移动的步数的最大值,disu 表示子树中所有点到 u 的距离之和。

那么很显然当且仅当 dpu×2=disu 时,u 是可以当做一个汇合点的,且贡献为 disu/2

那么假设 u 子树中最大的子树为 mxu,那么如果 mxudisumxu,那么肯定消最大的,然后会剩余 res=mxu(disumxu),然后剩余的部分就让其在其自己子树中消掉,那 dpx=(disxres)/2+min(res/2,dppx)px 为其最大子树。

如果 mxudisumxu,操作也一定是这个状态,那么最后一定剩下 0/1。转移同理。

然后考虑换根。

假设当前根为 x,要转移的儿子为 y,考虑将求以 y 为根的答案。

首先你要还要维护:mxx 最大儿子 mxxx 此大儿子 px 最大儿子编号 pxx 此大儿子编号 fax 最大儿子 dp 值,fbx 此大儿子 dp 值。之后说明为什么这么维护。

首先你要算出 x 作为 y 子树的大小,以及子树 xdp 值设为 dpx,需要分两种情况讨论:

  • 假如 yx 的重儿子,那么此时的重儿子为此重儿子,然后就用到了上面维护的 mxxxpxxfbx
  • 相反,会更简单一些。

更新 y 的各种值分三种情况:

  • 然后考虑 x 能否成为 y 的最大儿子;

  • 如果不行就考虑能否成为此大儿子

  • 均不可以;

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
const int inf=(1ll<<40);
char s[N];
bool flat[N];
int dp[N],siz[N],dis[N];
int mx[N],p[N];
int mxx[N],px[N];
struct asd{
    int fa,fb;
}e[N];
int head[N*2],ver[N*2],nex[N*2],tot=0;
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
void dfs(int x,int f){
    if(flat[x]) siz[x]=1;
    mx[x]=p[x]=0;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==f) continue;
        dfs(y,x);
        siz[x]+=siz[y];
        dis[x]+=(dis[y]+siz[y]);
        if(mx[x]<dis[y]+siz[y]){
            mx[x]=dis[y]+siz[y];
            e[x].fa=dp[y];
            p[x]=y;
        }
    }
    mxx[x]=0,px[x]=0;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==f || y==p[x]) continue;
        if(mxx[x]<dis[y]+siz[y]){
            mxx[x]=dis[y]+siz[y];
            e[x].fb=dp[y];
            px[x]=y;
        }
    }
    int res=0;
    if(mx[x]>(dis[x]-mx[x])) res=mx[x]-(dis[x]-mx[x]);
    else if(dis[x]%2) res=1;
    dp[x]=(dis[x]-res)/2+min(res/2,dp[p[x]]);
}
int ans=inf;
void tran(int y,int x){
    int res=0;
    int dpx;
    if(y!=p[x]){
        if(mx[x]>(dis[x]-(dis[y]+siz[y])-mx[x])) res=mx[x]-(dis[x]-(dis[y]+siz[y])-mx[x]);
        else if((dis[x]-(dis[y]+siz[y]))%2) res=1;
        dpx=(dis[x]-(dis[y]+siz[y])-res)/2+min(res/2,e[x].fa);
    }
    else{
        if(mxx[x]>(dis[x]-(dis[y]+siz[y])-mxx[x])) res=mxx[x]-(dis[x]-(dis[y]+siz[y])-mxx[x]);
        else if((dis[x]-(dis[y]+siz[y]))%2) res=1;
        dpx=(dis[x]-(dis[y]+siz[y])-res)/2+min(res/2,e[x].fb);
    }
    int F=siz[x]-siz[y]+dis[x]-(dis[y]+siz[y]);
    if(F>mx[y]){
        mxx[y]=mx[y];
        px[y]=p[y];
        e[y].fb=e[y].fa;
        mx[y]=F;
        p[y]=x;
        e[y].fa=dpx;
        dis[y]=F+dis[y];
        siz[y]=siz[x];
        int ress=0;
        if(mx[y]>dis[y]-mx[y]) ress=mx[y]-(dis[y]-mx[y]);
        else if(dis[y]%2) ress=1;
        dp[y]=(dis[y]-ress)/2+min(ress/2,e[y].fa);
    }
    else if(F>mxx[y]){
        px[y]=x;
        mxx[y]=F;
        e[y].fb=dpx;
        dis[y]=F+dis[y];
        siz[y]=siz[x];
        int ress=0;
        if(mx[y]>dis[y]-mx[y]) ress=mx[y]-(dis[y]-mx[y]);
        else if(dis[y]%2) ress=1;
        dp[y]=(dis[y]-ress)/2+min(ress/2,e[y].fa);
    }
    else{
        dis[y]=dis[y]+F;
        siz[y]=siz[x];
        int ress=0;
        if(mx[y]>dis[y]-mx[y]) ress=mx[y]-(dis[y]-mx[y]);
        else if(dis[y]%2) ress=1;
        dp[y]=(dis[y]-ress)/2+min(ress/2,e[y].fa);
    }
}
void solve(int x,int f){
    if(dp[x]*2==dis[x]) ans=min(ans,dis[x]/2);    
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==f) continue;
        tran(y,x);
        solve(y,x);
    }
}
signed main(){
    freopen("charlotte.in","r",stdin);
    freopen("charlotte.out","w",stdout);
    int n;
    scanf("%lld",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++) if(s[i]=='1') flat[i]=1;
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%lld%lld",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,0);
    solve(1,0);
    if(ans==inf) ans=-1;
    printf("%lld",ans);
}

chtholly#

首先我们定义关键点为以这个点为根,至少有三个儿子的最深深度大于等于 dist(s,t)

可以发现如果蛇的头或尾到达一个这样的关键点,就可以成功掉头。

对于关键点我们可以一遍 dfs 然后换根求出。

然后判断是否关键点是否可行,这时只需要判断一个,因为一个可行的话那么全部的一定都可行,否则都不可行

那么如何判断一个点是否可行,我们可以以这个点为根然后求出一个 px 表示当前点子树中最深的点是谁,然后没会都将一个点向最深的点跳,然后另一个点倍增跳到应属位置,如果合法的话,一个点会成为另一个点的祖宗。可以发现每回跳到的位置应该是递增的,所以次数不会太多。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int head[N*2],nex[N*2],ver[N*2],tot=0;
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int n,s,t,t1,len;
int mxd[N][5],p[N][5];
int f[N][30],d[N];
int st[N],top=0;
int ask_lca(int x,int y){
    if(d[x]>d[y]) swap(x,y);
    for(int i=t1;i>=0;i--)
        if(d[f[y][i]]>=d[x]) y=f[y][i];
    if(x==y) return x;
    for(int i=t1;i>=0;i--)
        if(f[y][i]!=f[x][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
void init(){
    t1=__lg(n)+1;
    for(int j=1;j<=t1;j++)
        for(int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
}
void dfs1(int x,int fa){
    d[x]=d[fa]+1;
    f[x][0]=fa;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs1(y,x);
        if(mxd[x][1]<mxd[y][1]+1){
            mxd[x][3]=mxd[x][2];
            p[x][3]=p[x][2];
            mxd[x][2]=mxd[x][1];
            p[x][2]=p[x][1];
            mxd[x][1]=mxd[y][1]+1;
            p[x][1]=y;
        }
        else if(mxd[x][2]<mxd[y][1]+1){
            mxd[x][3]=mxd[x][2];
            p[x][3]=p[x][2];
            mxd[x][2]=mxd[y][1]+1;
            p[x][2]=y;
        }
        else if(mxd[x][3]<mxd[y][1]+1 ){
            mxd[x][3]=mxd[y][1]+1;
            p[x][3]=y;
        }
    }
}
void dfs2(int x,int fa){
    if(mxd[x][1]>=len && mxd[x][2]>=len && mxd[x][3]>=len) st[++top]=x;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        if(y!=p[x][1]){
            if(mxd[x][1]+1>mxd[y][1]){
                mxd[y][3]=mxd[y][2];
                p[y][3]=p[y][2];
                mxd[y][2]=mxd[y][1];
                p[y][2]=p[y][1];
                mxd[y][1]=mxd[x][1]+1;
                p[y][1]=x;
            }
            else if(mxd[x][1]+1>mxd[y][2]){
                mxd[y][3]=mxd[y][2];
                p[y][3]=p[y][2];
                mxd[y][2]=mxd[x][1]+1;
                p[y][2]=x;
            }
            else if(mxd[x][1]+1>mxd[y][3]){
                mxd[y][3]=mxd[x][1]+1;
                p[y][3]=x;
            }
        }
        else{
            if(mxd[x][2]+1>mxd[y][1]){
                mxd[y][3]=mxd[y][2];
                p[y][3]=p[y][2];
                mxd[y][2]=mxd[y][1];
                p[y][2]=p[y][1];
                mxd[y][1]=mxd[x][2]+1;
                p[y][1]=x;
            }
            else if(mxd[x][2]+1>mxd[y][2]){
                mxd[y][3]=mxd[y][2];
                p[y][3]=p[y][2];
                mxd[y][2]=mxd[x][2]+1;
                p[y][2]=x;
            }
            else if(mxd[x][2]+1>mxd[y][3]){
                mxd[y][3]=mxd[x][2]+1;
                p[y][3]=x;
            }
        }
        dfs2(y,x);
    }
}
int mx[N],px[N];
void dfs3(int x,int fa){
    f[x][0]=fa;
    d[x]=d[fa]+1;
    px[x]=x;
    mx[x]=0;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs3(y,x);
        if(mx[x]<mx[y]+1){
            mx[x]=mx[y]+1;
            px[x]=px[y];
        }
    }
}
bool vis[N];
bool check(){
    int lca=ask_lca(s,t);
    if(s==lca || t==lca) return 1;
    for(int i=1;i<=2*n;i++){
        int x=px[s],len=d[x]-d[s];
        if(len>=d[t]-d[lca]) return 1;
        for(int j=t1;j>=0;j--){
            if(len>=(1<<j)){
                len-=(1<<j);
                t=f[t][j];
            }
        }
        s=x;
        swap(s,t);
    }
    return 0;
}
int main(){
    freopen("chtholly.in","r",stdin);
    freopen("chtholly.out","w",stdout);
    int T;
    scanf("%d",&T);
    int cnt=0;
    while(T--){
        cnt++;
        scanf("%d%d%d",&n,&s,&t);
        tot=0;
        top=0;
        memset(head,0,sizeof(int)*(n+1));
        memset(mx,0,sizeof(int)*(n+1));
        memset(vis,0,sizeof(bool)*(n+1));
        memset(mxd,0,sizeof(mxd));
        memset(p,0,sizeof(p));
        for(int i=1;i<n;++i){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y),add(y,x);
        }
        dfs1(1,0);
        init();
        int lca=ask_lca(s,t);
        len=d[s]+d[t]-2*d[lca];
        dfs2(1,0);
        if(!top){
            printf("NO\n");
            continue;
        }
        dfs3(st[1],0);
        init();
        if(check()) printf("YES\n");
        else printf("NO\n");
    }
}

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17814203.html

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

posted @   _bloss  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu