HGOI8.14集训题解

题解

又是一天爆零日orz
我已经尽力在概括题意啦..版权原因就不开放正式体面了!


第一题——capacitor(今天没有中文的题干

【题目描述】

  • 给出一个分数, ab a b ,给若干次操作,每次操作只能将两个数变成 c1+c2 c 1 + c 2 或者两个数变成 11c1+1c2 1 1 c 1 + 1 c 2 c1 c 1 必为操作之后的结果, c2 c 2 必为1,问多少次操作能够做出 ab a b
  • 给出若干组数据,每组数据为a,b两个数。

  • 刚开始看样例我还以为是max(a,b),后来才发现其实是辗转相除法。就是gcd的变形。
  • 每次加1的操作可以认为是 ab+1=a+bb a b + 1 = a + b b ,那么从 ab a b 进行一次操作之后的状态就变成了 abb a − b b
  • 同理,每次进行第二次的操作,将原式变形之后,就是 aba a b − a 那么多少次操作就是不断的大减小,就是一个gcd的变形。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("capacitor.in","r",stdin);
    freopen("capacitor.out","w",stdout);
}
LL gcd(LL a,LL b){
    if(b==0) return a;
    else return gcd(b,a%b);
}
int main(){
//  fff();
    int T;T=read();
    while (T--){
        LL a,b;
        scanf("%lld%lld",&a,&b);
        int _gcd=gcd(a,b);
        a=a/_gcd,b=b/_gcd;
        LL ans=0;
        while (a!=0&&b!=0){
            if(a==1){ans+=b;break;}
            if(b==1){ans+=a;break;}
            if(a==b){ans++; break;}
            if(abs(a-b)==1){
                ans+=max(a,b);
                break;
            }
            if(a<b) swap(a,b);
            ans+=a/b;
            a=a%b;
        }
        printf("%lld\n",ans);

    }
}

第二题——track

【题目描述】

  • 给出一段长度为n的跑道,初始高度为0,位置为0,每次走一步可以向上或者向下走一步,给出一段字符串,U为往上走,D为往下走,走的高度不能小于0,在路段当中必须有一段适合字符串是匹配的走法,最终终点的高度也为0,求走法的方案数。

  • 刚开始没有想全,认为只要有中间一段是合法的,那只要前面和后面的走法的方案数相乘再相加就可以了,而又有高度问题,又要枚举高度,本来以为能够骗一点分,但是没有考虑到路径中间如果有重复的走法(左右两端有走法和中间段的走法相重复),就会多解。
  • 然后就想到了KMP的算法,能够很好的解决重复路径的问题,而结果为了尽可能多做贡献,则要尽量回到字符串nxt的开头,上升和下降有不同的走法,也要注意。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("track.in","r",stdin);
    freopen("track.out","w",stdout);
}
const int MOD=1000000007;
int T,n,slen;
char s[210];
int nxt[210];
int fail[210][2];

int f[210][210][210],height[210];
void getfail(){
    int j=0;
    nxt[0]=nxt[1]=0;
    for (int i=1;i<slen;i++){
        while (j&&s[i]!=s[j]) j=nxt[j];
        if(s[i]==s[j]) j++;
        nxt[i+1]=j;
    }
    if(s[0]=='U') fail[0][0]=1;
    if(s[0]=='D') fail[0][1]=1;
    for (int i=1;i<=slen;i++){
        int pos=i;
        while(pos&&s[pos]!='U') pos=nxt[pos];
        fail[i][0]=pos+1;
        if(pos==0&&s[0]=='D') fail[i][0]=0;

        pos=i;
        while (pos&&s[pos]!='D') pos=nxt[pos];
        fail[i][1]=pos+1;
        if(pos==0&&s[0]=='U') fail[i][1]=0;
    }
}
int main(){
//  fff();
    T=read();
    if(T%2==1){
        printf("%d\n",0);
        return 0;
    }
    scanf("%s",&s);
    slen=strlen(s);
    T/=2;
    getfail();
    f[0][0][0]=1;
    for (int i=0;i<2*T;i++){
        for (int j=0;j<=T;j++){
            for (int k=0;k<slen;k++){
                if(s[k]=='U'){
                    f[i+1][j+1][k+1]=(f[i+1][j+1][k+1]+f[i][j][k])%MOD;
                    if(j) f[i+1][j-1][fail[k][1]]=(f[i+1][j-1][fail[k][1]]+f[i][j][k])%MOD;
                }else if(s[k]=='D'){
                    f[i+1][j+1][fail[k][0]]=(f[i+1][j+1][fail[k][0]]+f[i][j][k])%MOD;
                    if(j) f[i+1][j-1][k+1]=(f[i+1][j-1][k+1]+f[i][j][k])%MOD;
                }
            }
            f[i+1][j+1][slen]=(f[i+1][j+1][slen]+f[i][j][slen])%MOD;
            if(j)f[i+1][j-1][slen]=(f[i+1][j-1][slen]+f[i][j][slen])%MOD;
        }
    }
    int ans=f[2*T][0][slen];
    printf("%d",ans);
    return 0;
}

第三题——test

【这是一道模板题】

  • 给出一棵原式根为1的点权树,有q步操作:
  • 1、将树根的改为t,
  • 2、将某个节点修改为v
  • 3、求某个节点的子树和
  • 4、求两点之间的距离(点权和)。

  • 考场上除了树刨、树状数组、dfn序都想到了orz。
  • 树刨和树状数组在求和和修改的时候效率很高,板子,不讲
  • 在求子树和的时候,就只要判断当前的查询点在不在新的根和1(下称为源根)所在的路径上,如果不在,就无影响,按照正常来求,如果在,则求出sum[1]-sum[v(查询点在该条路径上的儿子)],具体自己画图体会
  • 求距离就不用lca了..树刨也能求orz,也是人生第一次遇到

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
}
const int N=1e5+10;
vector<int>G[N];
int a[N],n,q,sum=0;
int head[N],fa[N],pch[N];
int dep[N],in[N],out[N],tim=0;
int sz[N];
void dfs1(int u,int f){
    dep[u]=dep[f]+1;
    fa[u]=f;
    sz[u]=1;
    int mxsz=0,mxid=0;
    int siz=G[u].size();
    for (int i=0;i<siz;i++){
        int v=G[u][i];
        if(v==f) continue;
        dfs1(v,u);
        if(sz[v]>mxsz)mxsz=sz[v],mxid=i;
    }
    if(mxsz) swap(G[u][mxid],G[u][0]),pch[u]=G[u][0];
}

void dfs2(int x,int f,int h){
    in[x]=++tim;
    head[x]=h;
    bool ff=true;
    int siz=G[x].size();
    for (int i=0;i<siz;i++){
        int y=G[x][i];
        if(y==f) continue;
        dfs2(y,x,ff?h:y);
        ff=false;
    }
    out[x]=tim;
}
int t[N];
void add(int x,int v){
    for (;x<=n;x+=x&-x) t[x]+=v;
}
int query(int x){
    int res=0;
    for (;x;x-=x&-x) res+=t[x];
    return res;
}
int main(){
//  fff();
    n=read(),q=read();
    for (int i=1;i<n;i++){
        int u,v;
        u=read(),v=read();
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,0);
    dfs2(1,0,1);
    for (int i=1;i<=n;i++) a[i]=read(),sum+=a[i],add(in[i],a[i]);
    int rt=1;
    while (q--){
        int op;op=read();
        if(op==1) rt=read();
        else if(op==2){
            int x,v;
            x=read(),v=read();
            sum+=v-a[x];
            add(in[x],v-a[x]);
            a[x]=v;
        }else if(op==3){
            int x,ans=0;
            x=read();
            if(x==rt) ans=sum;
            else if(in[x]<in[rt]&&out[rt]<=out[x]){
                int lsth=0;
                int p=rt;
                while (head[p]!=head[x]) lsth=head[p],p=fa[lsth];
                if(p==x) p=lsth;else p=pch[x];
                ans=sum-(query(out[p])-query(in[p]-1));
            }else{
                ans=query(out[x])-query(in[x]-1);
            }
            printf("%d\n",ans);
        }else{
            int x,y,ans=0;
            x=read(),y=read();
            while (x!=y){
                if(head[x]==head[y]){
                    if(dep[x]>dep[y]) swap(x,y);
                    ans+=query(in[y])-query(in[x]),y=x;
                }else{
                    if(dep[head[x]]>dep[head[y]]) swap(x,y);
                    ans+=query(in[y])-query(in[head[y]]-1),y=fa[head[y]];
                }
            }
            ans+=a[x];
            printf("%d\n",ans);

        }

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值