zoukankan      html  css  js  c++  java
  • 10.08&&10.09模拟赛 订正题解

    今天看了发题解 发现出题人叫无解双八卦 orz

    这次不想粘题面了qwq

    10.08 100+100+50

    T1 有理树

    盯着图一直看 你会发现 向左走就是加上左边紧挨这个他的分子和分母 这里加的含义是 分子加分子 分母加分母 这里的左右是包含0/1和1/0那一列的

    所以我们记录 每个 左右数字是什么 模拟即可

    //考虑三个部分 0/1 1/1 1/0 
    //维护同一行 左 右的值 然后发现 向左走 就是 加上 左边的值
    //向右走 就是 加上 右边的值  这里的加指 分子+分子 分母+分母 
    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    const int N=5;
    struct gg {
        int fz,fm;
    }t[N];
    int a,b;
    inline void get_ans() {
        if(a==t[2].fz&&b==t[2].fm) return;
        double x=(1.0*a)/(1.0*b);
        double y=(1.0*t[2].fz)/(1.0*t[2].fm);
        if(x<y) {
            t[3].fz=t[2].fz; t[3].fm=t[2].fm;
            t[2].fz+=t[1].fz; t[2].fm+=t[1].fm;
            printf("L");
            get_ans();
            return;
        }
        else {
            t[1].fz=t[2].fz;t[1].fm=t[2].fm;
            t[2].fz+=t[3].fz;t[2].fm+=t[3].fm;
            printf("R");
            get_ans();
            return;
        }
    }
    int main() {
    //    freopen("1.in","r",stdin); 
        freopen("sbt.in","r",stdin);
        freopen("sbt.out","w",stdout);
        read(a); read(b);
        for(int i=2;i<=min(a,b);i++) {
            while(a%i==0&&b%i==0) {a/=i; b/=i;}
        }
        t[1].fz=0,t[1].fm=1;
        t[2].fz=1,t[2].fm=1;
        t[3].fz=1,t[3].fm=0;
        get_ans();
        return 0;
    }
    View Code

    T2 字符串

    考虑 他让你求的是什么 最大价值 而且 最开始 我先写的T2 发现 让某个东西拼起来 比如说 i 连向 j 并且会获得某某价值 j 再 连向 k 那么记录累加价值 更新了从 i 出发到 k 的最大价值

    观察这个过程 比较像 最长路更新的过程 不过你说是dp 也可以 毕竟图论也可以说是dp 

    那么边权就是题目给定的 那么考虑一个事情 我们不清楚 起点是什么 那么 暴力的想 枚举起点 

    但是对于这种多起点最短/长路的问题 我在CSP-S Day4的图论上总结了一部分 

    一种比较方便的做法就是 新建一个源点 连向所有起点 边权是0 然后考虑 跑一边最长路 并且判环即可

    floyed 写起来也比较方便

    //考虑将每个字符当成一个点 那么 就是求最长路 floyed 因为是多起点最长路 
    //新建源点0 连向每个点 
    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    const int N=150;
    int T,n,dis[N],a[N][N];
    int main() {
        freopen("string.in","r",stdin);
        freopen("string.out","w",stdout);
        read(T);
        while(T--) {
            read(n);
            for(int i=1;i<=n;i++) dis[i]=0;
            //memset(a,0,sizeof(a)); 
            for(int i=1;i<=n;i++) {
                for(int j=1;j<=n;j++) {
                    read(a[i][j]);
                }
            }
            for(int w=1;w<=n;w++) {
                for(int i=1;i<=n;i++) {
                    for(int j=1;j<=n;j++) {
                        dis[j]=max(dis[j],dis[i]+a[i][j]);
                    }
                }
            }//floyed 
    //        for(int i=1;i<=n;i++) cout<<dis[i]<<' ';
            int flag=1;
            for(int i=1;i<=n;i++) {
                for(int j=1;j<=n;j++) {
                    if(dis[j]<dis[i]+a[i][j]) 
                        flag=0;
                }
            }//判环
            if(flag) {
                int ans=0;
                for(int i=1;i<=n;i++)    ans=max(ans,dis[i]);
                cout<<ans<<endl;
            }
            else cout<<"-1"<<endl;
        }
        return 0;
    }
    View Code

    T3 序列

    考场上不会 写了ST表加速寻找最小值 复杂度N^2 其实同学说可以不这样写 在你向后移动 指针的时候 更新 最小值即可 呜呜呜没想到 st表还调了半天

    放个50的code

    //RMQ加速寻找最小值 满分不会写
    //19:39 我写完了三个题目 还能过大数据 很迷 今天估计要爆00000000了 
    //对拍写了一个小时 没搞出来 我又又又又忘了 
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    const int N=3000;
    int n,d,a[N],t,f[N][25];
    inline void RMQ() {
        for(int j=1;j<t;j++) {
            for(int i=1;i<=n-(1<<j)+1;i++) {
                f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
            }
        }
    } 
    inline int get(int x,int y) {
        int k=log(y-x+1)/log(2);
        return min(f[x][k],f[y-(1<<k)+1][k]);
    }
    int main() {
    //    freopen("1.in","r",stdin);
        freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        read(n); read(d);
        for(int i=1;i<=n;i++) {
            read(a[i]);
            f[i][0]=a[i];
        }
        t=log(n)/log(2)+1;
        RMQ();
    //    for(int i=1;i<=n;i++) {
    //        for(int j=1;j<=n;j++) {
    //            if(i>j) continue; 
    //            cout<<i<<' '<<j<<endl;
    //            cout<<get(i,j)<<" ";
    //            cout<<endl;
    //        }
    //    }
        ll ans=0;
        for(int i=1;i<=n;i++) {
            for(int j=i;j<=n;j++) {
                int minn=get(i,j);
                if((a[i]^a[j]^minn)==d) ans++;
            }
        }
        cout<<ans<<endl;
        return 0;
    } 
    View Code

    考虑满分做法怎么做 题解比我想的巧妙一点 考虑一个事情 

    我们可以确定全局最小值 然后确定 那些跨区间的最小值 然后 此时确定左端点 然后确定最小值 根据异或的性质 我们就可以确定另外一个端点对应的值

    所以 我们维护一个单调递增的栈 求出来每个数字作为最小值是对应的 下标 然后考虑开一个vector记录每个数值出现的位置 枚举我们枚举端点的时候 我们就可以 

    二分查找出来当前端点能在我当前区间出现的合法次数 

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    int sta[100005];
    vector<int>g[1100000];
    int n,d,top,a[100005],ll[100005],rr[100005];
    long long ans;
    inline void solve(int mid,int l,int r) {
        if(mid-l<r-mid) {
            for(int i=l;i<=mid;++i) {
                int v=d^a[i]^a[mid];
                ans+=lower_bound(g[v].begin(),g[v].end(),r+1)-lower_bound(g[v].begin(),g[v].end(),mid);
            }
        }
        else {
            for(int i=mid;i<=r;++i) {
                int v=d^a[i]^a[mid];
                ans+=lower_bound(g[v].begin(),g[v].end(),mid+1)-lower_bound(g[v].begin(),g[v].end(),l);
            }
        }
        if(ll[mid])solve(ll[mid],l,mid-1);
        if(rr[mid])solve(rr[mid],mid+1,r);
    }
    int main() {
        //freopen("1.in","r",stdin);
        freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        read(n); read(d);
        for(int i=0;i<100005;++i) g[i].clear();
        for(int i=1;i<=n;i++) {
            read(a[i]);g[a[i]].push_back(i);
            if(!top||a[i]>=a[sta[top]]) sta[++top]=i;
            else {
                for(;top&&a[i]<a[sta[top]];--top) rr[sta[top-1]]=sta[top];
                ll[i]=sta[top+1];sta[++top]=i;
            }
        }
        for(;top;--top) rr[sta[top-1]]=sta[top];
        //cout<<rr[0]<<endl;
        solve(rr[0],1,n);
        printf("%lld
    ",ans);
        return 0;
    } 
    View Code

    10.09 100+100+50

    T1分组

    考虑gcd的东西 我们假设一种情况 我们为了让当前这个数字 留下来能够成为某次的答案 那么我一定安排他和相同的数字 或者 他的倍数放一块 那么假设这些一共出现了num次

    并且没有其他数字出现次数和他相同 那么 我们 就可以考虑 当num个人一起的时候 最大的gcd 应该是就是我当前枚举的数字了

    那么 数字范围只有1e6 我们完全可以开桶来记录 每个数字出现的次数 这也算是 我在青岛学到的新名词 说实话应该早就会的

    所以直接寻找倍数 把每个数字和每个数字的倍数出现的次数记录下来 那么直接更新答案 更新的时候注意和比他大一的答案进行比较 

    因为如果当前答案小 那么你总能从前者抽出若干个人 满足当前的答案是 前者的答案

    //考虑开桶记录每个数值出现的次数
    //因为是求gcd 然后 考虑 找到每个数字以及他的倍数 出现的次数
    //最后ans取max即可 
    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    const int N=1010000;
    int n,x,num[N],ans[110000]; 
    int main() {
    //    freopen("1.in","r",stdin);
    //    freopen("1.out","w",stdout);
        freopen("group.in","r",stdin);
        freopen("group.out","w",stdout);     
        read(n);
        int maxx=0;
        for(int i=1;i<=n;i++) {
            read(x);
            num[x]++; 
            maxx=max(maxx,x);
        }
        for(int i=1;i<=maxx;i++) {
            for(int j=2*i;j<=maxx;j+=i) {
                num[i]+=num[j];
            }
        } 
        for(int i=1;i<=maxx;i++) {
             ans[num[i]]=i;
        }
        for(int i=n;i;i--)  {
            ans[i-1]=max(ans[i-1],ans[i]);//考虑此时一定存在 有的答案是0 但是这显然是不优秀的
            //考虑此时将ans[i-1]的答案更新为ans[i]
        }
        for(int i=1;i<=n;i++) printf("%d
    ",ans[i]); //不敢用cout 害怕T_T 
        return 0;
    }
    View Code

    T2 迷宫

    啥鬼东西 写了两个小时 反正那个状压真的是 写起来我觉得比较难受 但是某某强者 说很好写 

    设状态 F[S][i] 表示在当前S集合的情况下 到达 i 的最小代价 初始化为每次dij 跑出来的单源最短路

    那么还需要统计出来 每一个朋友被困的位置 之间的相互距离 每次dij之后更新即可 dist数组表示

    此时更优的做法一定是 就完最后一个朋友之后 就一起逃离迷宫 所以更新每个被困的朋友 能够逃出来迷宫的 最短距离 这里我用zh数组表示

    那么考虑状态转移 其实很简单 每次lowbit一下 求出来上一个状态的人数 更新当前的人数 然后 考虑转移 判断当前的朋友有没有被救出来

    考虑先救 j 然后从 j 跑到 i 每次取min即可 而且我发现一个坑就是 判断第i位是否为0 那么位移(i-1)qwq 我状压水平好垃圾

    //dij+状态压缩 跑单源最短路 
    //好像枚举起点只有部分分吧qwq 
    //这个dp我写了快两个小时 T3 估计要咕了 
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    const int N=60000;
    const ll inf=1e18;
    struct gg {
        ll y,next,v;
    }a[N<<1];
    struct pink {
        ll pos,dis;
        bool operator<(const pink &a)const{
            return dis>a.dis;
        }
    }; 
    priority_queue<pink>q; 
    inline ll lowbit(ll x) {return x&-x;}
    int n,m,k,s,p[N],vis[N];
    ll tot,x,y,v;
    ll dis[N],lin[N],d[N],rs[1<<21],f[21][(1<<21)],zh[21],dist[21*2][21*2];//rs数组开小了(wa1) 
    inline void add(ll x,ll y,ll v) {
        a[++tot].y=y;
        a[tot].v=v;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    void dij(int s) {
        for(int i=1;i<=n;++i) {
            vis[i]=0;dis[i]=inf;
        }
        dis[s]=0;
        pink h,p;
        h.pos=s;h.dis=0;
        q.push(h);
        while(!q.empty()) {
            while(!q.empty()&&vis[q.top().pos]) q.pop();
            //if(q.empty()) break;
            h=q.top();q.pop();
            int x=h.pos;
            ll v=h.dis;
            vis[x]=1;
            for(int i=lin[x];i;i=a[i].next) {
                int y=a[i].y;
                if(dis[y]>v+a[i].v) {
                    dis[y]=v+a[i].v;
                    p.pos=y,p.dis=dis[y]; 
                    q.push(p);
                }
            }
        }
    }
    int main() {
        freopen("1.in","r",stdin);
        //freopen("maze.in","r",stdin);
        //freopen("maze.out","w",stdout);
        read(n); read(m); read(k); read(s); //n个点 m条边 k个同伴(起点) s为bagua出发点  
        for(int i=1;i<=n;++i) read(p[i]);
        for(int i=1;i<=m;++i) {
            read(x); read(y); read(v);
            add(x,y,v); add(y,x,v);
        }
        for(int i=1;i<=k;++i) read(d[i]);
        for(int i=1;i<=k;++i) {
            dij(d[i]);
            f[i][1<<(i-1)]=dis[s];
            zh[i]=inf;
            for(int j=1;j<=n;++j) {
                if(p[j]) {
                    zh[i]=min(zh[i],dis[j]); 
                }
            }
            for(int j=1;j<=k;++j) {
                dist[i][j]=dis[d[j]];
    //            printf("%lld
    ",dis[d[j]]);
            }
        }
        /*for(int S=1;S<(1<<k);S++) {//这个dp好难啊 
            int pre=S-lowbit(S);
            rs[S]=rs[pre]+1;
            if(!pre) continue; 
            /*for(int i=1;i<=k;++i) {
                f[i][S]=inf;
                if(!((S>>i)&1)) continue;//判断当前i有没有被救出来 
                for(int j=1;j<=k;++j) {//下标从0开始(wa1) 
                    if(i!=j&&((S>>(j-1))&1)) {
                        f[i][S]=min(f[i][S],f[j][S-(1<<(i-1)]+dist[i][j]*rs[S]);
                    }    
                }  
            }
        }*/
        for(int S=1;S<(1<<k);++S) { //我再写一遍 
            int pre=S-lowbit(S);//pre 表示 S-lowbit(S) 表示去掉最后一位的1 之后 状态
            rs[S]=rs[pre]+1; //此时人数+1 多救了一个朋友 renshu+1
    //        cout<<rs[S]<<endl;
            if(pre==0) continue;
            for(int i=1;i<=k;++i) {
                f[i][S]=inf;
                if(!((S>>(i-1))&1))continue;
                for(int j=1;j<=k;++j) {
                    if(i!=j&&((S>>(j-1))&1)) {
                        f[i][S]=min(f[i][S],f[j][S-(1<<(i-1))]+dist[i][j]*rs[S]);
    //                    printf("%d
    ",f[i][S]);
                    }
                }  
            }
        }
    //  cout<<zh[1]<<endl;
        ll ans=inf;
        for(int i=1;i<=k;++i) ans=min(ans,f[i][(1<<k)-1]+zh[i]*(k+1));
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    T3 挖矿

    oi不努力 来年去挖矿

    考虑 一定是先枚举lca的链深度较小的链 那么考虑什么时候第二条链会对第一条链产生贡献 一定是lca连出的边必定恰好有一条边在第一条链上

    那么我们就可以把当前链的lca连出的边的赋上一个权值 为当前链的的贡献

    最后枚举一遍 所有的链 考虑倍增维护权值和

    //枚举链的情况 那么肯定是比较二者的lca
    //不用枚举两条的链的情况 那么一定是先枚举lca较小的链 
    //考虑倍增维护 跳2^j 所到达的点 期间经过的链的权值 
    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    const int N=110000;
    struct gg {
        int y,next,v;
    }a[N<<1];
    struct pink {
        int x,y,w,lca;
    }s[N];
    int n,m,tot,x,y,v;
    int f[N][25],w[N][25],d[N],lin[N]; 
    inline void add(int x,int y) {
        a[++tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    inline void dfs(int x) {
        d[x]=d[f[x][0]]+1;
        for(int j=1;j<=23;j++) f[x][j]=f[f[x][j-1]][j-1];
        for(int i=lin[x];i;i=a[i].next) {
            dfs(a[i].y);
        }
    }
    inline int lca(int x,int y) {
        if(d[x]>d[y]) swap(x,y);
        for(int i=23;i>=0;i--) {
            if(d[f[y][i]]>=d[x]) {
                y=f[y][i];
            }
        }
        if(y==x) return x;
        for(int i=23;i>=0;i--) {
            if(f[x][i]!=f[y][i]) {
                x=f[x][i],y=f[y][i];
            }
        }
        return f[x][0];
    }
    inline int get(int x,int de) {
        for(int i=23;i>=0;--i) {
            if(d[f[x][i]]>=de) {
                x=f[x][i];
            }
        }
        return x;
    }
    inline int get_w(int x,int de) {
        int res=0;
        for(int i=23;i>=0;i--) {
            if(d[f[x][i]]>=de) {
                res+=w[x][i];
                x=f[x][i];    
            } 
        }
        return res;
    }
    inline void dfss(int x) {
        for(int i=1;i<=23;i++) w[x][i]=w[x][i-1]+w[f[x][i-1]][i-1];
        for(int i=lin[x];i;i=a[i].next) {
            dfss(a[i].y);
        }
    }
    int main() {
    //    freopen("1.in","r",stdin);
    //    freopen("1.out","w",stdout); 
        freopen("mine.in","r",stdin);
        freopen("mine.out","w",stdout);
        read(n); read(m);
        for(int i=2;i<=n;i++) {
            read(f[i][0]);
            add(f[i][0],i);
        }
        dfs(1);
        for(int i=1;i<=m;i++) {
            read(s[i].x); read(s[i].y); read(s[i].w);
            s[i].lca=lca(s[i].x,s[i].y);
    //        cout<<s[i].lca<<endl;
            if(s[i].x!=s[i].lca) w[get(s[i].x,d[s[i].lca]+1)][0]+=s[i].w;
    //        cout<<get(s[i].x,d[s[i].lca]+1)<<endl;
            if(s[i].y!=s[i].lca) w[get(s[i].y,d[s[i].lca]+1)][0]+=s[i].w;
        }
        dfss(1);
        int res=0;
        for(int i=1;i<=n;i++) {
            int ans=s[i].w;
            if(s[i].x!=s[i].lca) ans+=get_w(s[i].x,d[s[i].lca]+1);
            //cout<<get_w(s[i].x,d[s[i].lca]+1)<<endl;
            if(s[i].y!=s[i].lca) ans+=get_w(s[i].y,d[s[i].lca]+1); 
            res=max(ans,res);
        }
        cout<<res<<endl;
        return 0;
    } 
    View Code
  • 相关阅读:
    iPhone将NSString转换编码集为gb2312或者gbk的方法
    iOS下微信语音播放之切换听筒和扬声器的方法解决方案
    苹果开发者各地区联系电话
    iOS开发中,应用内直接跳转到Appstore
    高亮显示UILabel中的子串
    UML
    All Classic Bluetooth profile for iPhone
    智能穿戴设备移动APP端与外设数据传输协议功能模块CMD&ACK表
    Mac 使用技巧分享
    git branch管理小结
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11646448.html
Copyright © 2011-2022 走看看