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\n",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\n",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\n",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\n",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\n",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

 

posted @ 2019-10-10 10:46  Tyouchie  阅读(127)  评论(0编辑  收藏  举报