8.2省选模拟总结

好吧,考得时候感觉第二题比较简单就直接切了(看到大家都瞄准第一题狠打的样子真吓坏了。。)
结果160第一收场。。。

第一题 追捕盗贼
题目大意:
给定无向连通图(无重边)
两种询问:
1、删除a、b间的边(直接相连的边)后,c能否到d
2、删除点a后,c能否到d

(设定:以下我已经用tarjan构成一颗树,并求出deep,low,dfn值)
那么我们可以用tarjan中的low和dfn来判定。
对于1,若删除a、b,c能到d,有几种情况:
一、a、b在一个环上,那么就有(设deep[a]<deep[b])也就是tarjan里a是b的前继,有 low[b]≤dfn[a]
二、a、b不在c到d的路径上,这个可以用lca判,a、b不在c到lca(c、d)到d的路径上(链剖dfs序判断)
(注意删除的边就是连着c或d或都连着)
可证明这是正确的(提示:反证法)

2.若删除a,c能到d,则有:
一、a不在c到d的路径上
二、设e和f,e为c的lca、lca(c、d)的最近子节点,f为d的lca、lca(c、d)的最近子节点,a=lca(c,d)(反过来也可),有max(low[e],low[f])<dfn[a],若只有c或d为e的子节点(设为c,e仍存在),则有:low[e]<dfn[a]
特判a=c或a=d

贴代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100001
#define E 500001
using namespace std;
int q,n,m;
int stack[N][2],a[E*2][2],g[N],deep[N],dfn[N],low[N],fa[N][18];
bool bz[N],iscut[N],ans[2];
void ins(int x,int y){
    static int sum=0;
    a[++sum][0]=y,a[sum][1]=g[x],g[x]=sum;
}
void init(){
    static int x,y;
    scanf("%d %d",&n,&m);
    for (int i=1;i<=m;i++)
        scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
}
void tarjan(int x){
    static int sum=0,y;
    low[x]=dfn[x]=++sum;
    deep[x]++;
    for (int i=0;y=fa[fa[x][i]][i];fa[x][++i]=y);
    for (int i=g[x];i;i=a[i][1])
        if (fa[x][0]!=a[i][0]){
            int v=a[i][0];
            if (!dfn[v]){
                deep[v]=deep[x],fa[v][0]=x;
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }else
                low[x]=min(low[x],dfn[v]);
        }
    deep[x]--;
}
void pre(){
    deep[1]=1;
    tarjan(1);
}
int getlca(int x,int y){
    static int i;
    i=17;
    if (deep[x]<deep[y])swap(x,y);
    while (deep[x]!=deep[y]){
        for (;deep[fa[x][i]]<deep[y];i--);
        x=fa[x][i];
    }
    i=17;
    while (x!=y){
        for (;fa[x][i]==fa[y][i]&&i;i--);
        x=fa[x][i],y=fa[y][i];
    }
    return x;
}
int up(int x,int y){
    static int i;
    i=17;
    if (deep[y]>=deep[x])return 0;
    while (!(deep[x]-1==deep[y])){
        for (;deep[fa[x][i]]<=deep[y];i--);
        x=fa[x][i];
    }
    return fa[x][0]==y?low[x]:0;
}
void work(){
    static int x,y,s,t,lca;
    static bool e1,e2;
    scanf("%d",&q);
    for (;q--;){
        ans[0]=ans[1]=0;
        scanf("%d %d %d",&x,&s,&t);
        lca=getlca(s,t);
        if (x==1){
            scanf("%d %d",&x,&y);
            if (deep[x]<deep[y])swap(x,y);
            if (deep[s]<deep[t])swap(s,t);
            if (s==x&&t==y){
                if (low[x]<=dfn[y])printf("yes\n");
                else
                    printf("no\n");
            }else
                if (low[x]<=dfn[y]||deep[x]<deep[lca]||deep[y]<deep[lca])printf("yes\n");
                else{
                    e1=(up(s,x)||up(t,x)||t==x||s==x);
                    e2=(up(s,y)||up(t,y)||t==y||s==y);
                    if (e1&&e2)printf("no\n");else printf("yes\n");
                }
        }else{
            scanf("%d",&x);
            if (deep[lca]>deep[x])printf("yes\n");
            else
                if (max(up(s,x),up(t,x))<dfn[x])printf("yes\n");
                else
                    printf("no\n");
        }
    }
}
int main(){
    init();
    pre();
    work();
    return 0;
}

第二题 道路重组
题目大意
N个点N-1条无向边的组成的连通图,现在允许你删除一条边,再添加一条边,在保证连通的前提下,使得最远的两个点之间的距离尽可能小。

由于有诸多判定,我交了四次…最后终于过了(考的时候,虽说当时我没对拍,不知道…其实是没想到暴力…)

最远点,我们可以想到是树的直径,
那么删边我们可以看成将子树i,截出来,再将子树i与原树剩余部分的直径的中点连起来(这样可证为该决策下最优解,由于接起来不能改变原树剩余部分与子树i的直径,只能让通过他们连接的路径尽可能短)
那么我们可以用树形dp来做。
我们要维护子树最长、次长直径,子树最深、次深、次次深点到该子树根的路径,从前面搜来时记录的除该树外其余分支的最长直径、连到该子树的最长路径…看起来有点麻烦的说…但打起来时很有调理,所以还是可以打的。
贴代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 300001
int n,ans;
int g[N],a[N*2][2],b[N][3],from[N][3],len[N][2],back[N][2];
void ins(int x,int y){
    static int sum=0;
    a[++sum][0]=y,a[sum][1]=g[x],g[x]=sum;
}
void init(){
    static int x,y;
    scanf("%d",&n);
    for (int i=1;i<n;i++)
        scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
}
void did(int x,int y,int z){
    if (len[x][0]<y){
        len[x][1]=len[x][0],back[x][1]=back[x][0];
        len[x][0]=y,back[x][0]=z;
    }else
        if (len[x][1]<y)
            len[x][1]=y,back[x][1]=z;
}
void dfs(int x,int fa){
    for (int i=g[x];i;i=a[i][1])
        if (a[i][0]!=fa){
            dfs(a[i][0],x);
            static int y;
            y=a[i][0];
            if (b[y][0]+1>b[x][0]){
                b[x][2]=b[x][1],from[x][2]=from[x][1];
                b[x][1]=b[x][0],from[x][1]=from[x][0];
                b[x][0]=b[y][0]+1,from[x][0]=y;
            }else
                if (b[y][0]+1>b[x][1]){
                    b[x][2]=b[x][1],from[x][2]=from[x][1];
                    b[x][1]=b[y][0]+1,from[x][1]=y;
                }else
                    if (b[y][0]+1>b[x][2])
                        b[x][2]=b[y][0]+1,from[x][2]=y;
            did(x,max(len[y][0],b[y][0]+b[y][1]+1),y);
        }
}
void pre(){
    dfs(1,0);
}
void treedp(int x,int y,int z,int fa){
    static int s,s1;
    s=max(len[x][0],b[x][0]+b[x][1]+1);
    y=max(y,z);
    if (x!=1)ans=min(ans,max(s,max(y,y/2+1+s/2+1)));
    z++;
    for (int i=g[x];i;i=a[i][1])
        if (a[i][0]!=fa){
            static bool e1,e2,e3;
            e1=(a[i][0]!=from[x][0]&&from[x][0]),e2=(a[i][0]!=from[x][1]&&from[x][1]),e3=(a[i][0]!=from[x][2]&&from[x][2]);
            if (back[x][0]==a[i][0])s=len[x][1];else s=len[x][0];
            if (e1&&e2)
                treedp(a[i][0],max(s,max(y,max(z,b[x][1]+1)+b[x][0])),max(z,b[x][0]+1),x);
            else
                if (e1&&e3)
                    treedp(a[i][0],max(s,max(y,max(z,b[x][2]+1)+b[x][0])),max(z,b[x][0]+1),x);
                else
                    if (e2&&e3)
                        treedp(a[i][0],max(s,max(y,max(z,b[x][2]+1)+b[x][1])),max(z,b[x][1]+1),x);
                    else
                        if (!e1&&!e2&&!e3)
                            treedp(a[i][0],y,z,x);
                        else{
                            if (e1)s1=0;else if(e2)s1=1;else s1=2;
                            treedp(a[i][0],max(s,max(y,z+b[x][s1])),max(z,b[x][s1]+1),x);
                        }
        }
}
void work(){
    ans=b[1][0]+b[1][1]+1;
    treedp(1,0,0,0);
}
void write(){
    printf("%d",ans-1);
}
int main(){
    init();
    pre();
    work();
    write();
    return 0;
}

第三题 阿凡达
题目大意
对数列a进行两种操作:
1 L R A B:对于编号为L到R之间的每一个数,假设编号为X,重新设置第X个数为(X-L+1)*A mod B,即第L个数设置为A mod B,第L+1个数设置为2*A mod B,依次类推下去;

2 L R:输出数列中第L个数到第R个数的和。

暴力30…

题目最大难点在于如何求:
i*A%B
转化一下可得:

= i*A- iA/B *B
那么变成只求
iA/B
对于这个我们可以设成get(N,A。B,C),表示:
N0iA+C/B
分三种情况:
1、A≥B,原式= N0A/B *i+ N0C/B + N0 (A%B*i+C%B)/B

=A/B*(n+1)N/2+C/B(N+1)+get(N,A%B,B,C%B)

2、A<B
证明:设Y= (Ai+C)/B
Ni=0Y1j=0(j<Y)=>Y1j=0Ni=0(j<Y)=>Y1j=0N(jB+BC1)/A=>YNget(Y1,B,A,BC1)

中间的证明完善:
考虑何时(j<Y)
j+1≤(A*i+C)/B
j*B+B≤A*i+C
(j*B+B-B)/A≤i

注意除法为下取整,不能换位,加上线段树区间复制与查询
贴代码,其余细节处理好就行了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define M 50001
using namespace std;
int n,m,top;
int a[M][5],b[M+M],c[M*4][2];
struct node{
    __int128 A,B,L,v;
}f[M*16];
void init(){
    scanf("%d %d",&n,&m);
    for (int i=1;i<=m;i++){
        scanf("%d %d %d",&a[i][0],&a[i][1],&a[i][2]);
        if (a[i][0]==1)scanf("%d %d",&a[i][3],&a[i][4]);
        b[++b[0]]=a[i][1],b[++b[0]]=a[i][2];
    }
}
void pre(){
    static int sum;
    sort(b+1,b+b[0]+1);
    sum=b[0];
    b[0]=1;
    for (int i=2;i<=sum;i++)
        if (b[i]!=b[b[0]])b[++b[0]]=b[i];
    for (int i=1;i<b[0];i++){
        c[++top][0]=b[i],c[top][1]=b[i],c[++top][0]=b[i]+1,c[top][1]=b[i+1]-1;
        if (c[top][0]>c[top][1])top--;
    }
    c[++top][0]=b[b[0]],c[top][1]=b[b[0]];
}
__int128 get(__int128 N,__int128 A,__int128 B,__int128 C){
    if (!A)
        return C/B*N;
    if (A>=B)
        return A/B*(N+1)*N/2+C/B*(N+1)+get(N,A%B,B,C%B);
    else
        return ((A*N+C)/B)*N-get((A*N+C)/B-1,B,A,B-C-1);
}
void down(int ll,int rr,int s){
    static int ss,l,r;
    if (f[s].A){
        l=c[ll][0],r=c[rr][1];
        ss=r-l+1;
        f[s].v=f[s].A*(ss+f[s].L+f[s].L+1)*ss/2-(get(ss+f[s].L,f[s].A,f[s].B,0)-get(f[s].L,f[s].A,f[s].B,0))*f[s].B;
        if (ll!=rr)
            f[s+s]=f[s+s+1]=f[s],f[s+s+1].L+=c[(ll+rr)/2+1][0]-l;
        f[s].A=0;
    }
}
__int128 find(int l,int r,int s,int ll,int rr){
    down(l,r,s);
    if (c[r][1]<ll||rr<c[l][0])return 0;
    if (ll<=c[l][0]&&c[r][1]<=rr)return f[s].v;
    __int128 sum=find(l,(l+r)/2,s+s,ll,rr)+find((l+r)/2+1,r,s+s+1,ll,rr);
    return sum;
}
void ins(int l,int r,int s,int ll,int rr,int x,int y){
    down(l,r,s);
    if (c[r][1]<ll||rr<c[l][0])return;
    if (ll<=c[l][0]&&c[r][1]<=rr){
        f[s].A=x,f[s].B=y,f[s].L=c[l][0]-ll;
        down(l,r,s);
        return;
    }
    ins(l,(l+r)/2,s+s,ll,rr,x,y),ins((l+r)/2+1,r,s+s+1,ll,rr,x,y);
    f[s].v=f[s+s].v+f[s+s+1].v;
}
void work(){
    for (int i=1;i<=m;i++)
        if (a[i][0]==1)
            ins(1,top,1,a[i][1],a[i][2],a[i][3],a[i][4]);
        else{
            long long ans;
            ans=find(1,top,1,a[i][1],a[i][2]);
            printf("%lld\n",ans);
        }
}
int main(){
    init();
    pre();
    work();
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值