机房测试9.22

题解之前

中秋只有一天假!

build

以为并查集水题,发现有历史版本,于是可持久化并查集一波(爆0了)

其实有简单点的做法:用二元组记录每一次变化的siz和fa,二分查询即可。

我还在肝可持久并查集,所以就没有代码了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN=100005 ;
int n, m, fa[MAXN], times[MAXN], h[MAXN], nowT, size[MAXN];

struct Info{
    int tm, size ;
    Info(int _tm=0,int _size=0){ tm=_tm , size=_size; }
};

vector<Info> vec[MAXN] ;
vector<Info>::iterator it ;

bool cmp(const Info &a, const Info &b){
    return a.tm < b.tm ;
}

int findFa(int a){
    if(fa[a]==a) return a;
    return findFa(fa[a]) ;
}

void mergeNodes(int u,int v){
    int fu=findFa(u), fv=findFa(v) ;
    if(fu==fv) return ;
    if(h[fu]<h[fv]) swap(fu,fv) ;
    fa[fv]=fu, size[fu]+=size[fv], times[fv]=nowT ;
    if(h[fu]==h[fv]) h[fu]++ ;
    vec[fu].push_back(Info(nowT,size[fu])) ;
}

int query(int t, int a){
    while(a!=fa[a] && times[a]<=t) a=fa[a] ;
    it=upper_bound(vec[a].begin(),vec[a].end(),Info(t,0),cmp) ;
    --it ;
    return it->size ;
}

int main(){
    freopen("build.in","r",stdin) ;
    freopen("build.out","w",stdout) ;
    scanf("%d%d",&n,&m) ;
    for(int i=1;i<=n;++i) fa[i]=i, vec[i].push_back(Info(0,1)), h[i]=1, size[i]=1 ;
    int lastans=0 ;
    for(int i=1;i<=m;++i){
        int t, u, v ;
        nowT=i ;
        scanf("%d%d%d",&t,&u,&v) ;
        u+=lastans , v+=lastans ;
        if(t==1){
            mergeNodes(u,v) ;
        }
        else if(t==2){
            printf("%d\n",lastans=query(u,v)) ;
        }
    }
    return 0 ;
}

清华爷的代码就是不一样。

distribute

我真的不认识这个单词。

但是这道题水的惊人。

dp[i] 表示先手在第 i 个物品时的最大收益。

因为此题有后效性,选择从后往前for。

如果上一手换了,就是sum[i+1]-dp[i+1]+a[i](sum为前缀和);

如果没有换,就是dp[i+1]。

取max即可。

ye!

其实可以从dp[i][0/1][0/1]慢慢来,总之只要知道必须从后往前,基本就对了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define FN "distribute"

const int maxn=1e5+5;

int a[maxn];
long long dp[maxn],sum[maxn];

int main() {
    freopen(FN".in","r",stdin);
    freopen(FN".out","w",stdout);
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    dp[n]=sum[n]=a[n];
    for(int i=n-1;i>0;i--) {
        sum[i]=sum[i+1]+a[i];
        dp[i]=std::max(dp[i+1],sum[i+1]-dp[i+1]+a[i]);
    }
    printf("%lld",dp[1]);
    return 0;
}

find

讲得好!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=100005 , MAXM=MAXN<<1 ;
int n , m , first[MAXN] , nexts[MAXM] , to[MAXM] , egnum=1 ;
int TTT, low[MAXN], dfn[MAXN], top, kNum, tag[MAXN], q[MAXM] , qnum , stk[MAXM] ;
bool isAns[MAXM], vis[MAXM] ;

void addEdge(int a,int b){
    nexts[++egnum]=first[a] , first[a]=egnum , to[egnum]=b ;
}

void tarjan(int a,int fEdge){
    dfn[a]=low[a]=++TTT ;
    for(int i=first[a];i;i=nexts[i]){
        int b=to[i] ;
        if((i^1)==fEdge) continue ;
        if(!vis[i]) vis[i]=vis[i^1]=true , stk[++top]=i ;
        if(!dfn[b]){
            tarjan(b,i) ;
            low[a]=min(low[a],low[b]) ;
            if(low[b]>=dfn[a]){
                ++kNum , qnum=0 ;
                int cnt=0, t;
                do{
                    t=stk[top--] ;
                    if(tag[to[t]]!=kNum) tag[to[t]]=kNum , ++cnt ;
                    if(tag[to[t^1]]!=kNum) tag[to[t^1]]=kNum , ++cnt ;
                    q[++qnum]=t ;
                }while(t!=i);
                if(qnum==cnt){
                    for(int i=1;i<=qnum;++i){
                        isAns[q[i]]=isAns[q[i]^1]=true ;
                    }
                }
            }
        }
        else if(dfn[b]<low[a]){
            low[a]=min(low[a],dfn[b]) ;
        }
    }
}

int main(){
    freopen("find.in","r",stdin) ;
    freopen("find.out","w",stdout) ;
    scanf("%d%d",&n,&m) ;
    for(int i=1;i<=m;++i){
        int a,b;
        scanf("%d%d",&a,&b) ;
        addEdge(a,b) , addEdge(b,a) ;
    }
    for(int i=1;i<=n;++i){
        if(!dfn[i]){
            tarjan(i,0) ;
        }
    }
    int cnt=0 ;
    for(int i=2;i<=egnum;i+=2) if(isAns[i]) ++cnt ;
    printf("%d\n",cnt);
    for(int i=2;i<=egnum;i+=2) if(isAns[i])
        printf("%d ",i/2) ;
    printf("\n") ;
    return 0 ;
}

end

原文地址:https://www.cnblogs.com/LoLiK/p/9690309.html

时间: 2024-10-06 00:53:33

机房测试9.22的相关文章

推荐书籍 -《移动App测试的22条军规》

在今天的博文中,博主希望给大家分享一本博主同事黄勇的最新利作:<移动App测试的22条军规>.黄勇是ThoughtWorks资深敏捷QA和咨询师.对于我来说,和黄勇在一起的工作的这个项目,是我至今所一直怀念的那种少有的项目.黄勇在团队中以资深QA的团队协调能力和专业技能,不仅保障了项目的交付质量,同时也能很好的协调从客户到开发中的各个环节. 移动互联网的兴起 在当今世界,移动互联网已经兴起了,它距离我们大家,已经不再那么遥远了,已经开始慢慢的融入了我们的生活之中.特别在最近两年,BAT这等巨头

Host1Plus主机商8个机房测试IP体验

Host1Plus商家也算是比较老的海外主机商(英国),当初进入中国市场也是比较早的,记得那时候支持支付宝的海外主机商尤其的深受用户喜欢.因为我 们大部分网友.站长最多有一个支付宝,很少有双币信用卡或者贝宝支付美元的能力.不过后来几年,HOST1PLUS商家逐渐的落寞,主要原因在于其他商家 的出现,以及他们本身营销能力和产品的策略. 尤其是提供的虚拟主机和VPS主机,价格和方案不是太符合中国用户的口味,配置比较低,而且机房较 少.不过商家应该意识到这一点,在最近2年改动挺大的,今天公司业务正好有

[20191004机房测试] ZGY的早餐

ZGY 每天早上要从宿舍走路到机房,顺便从学校小卖部购买早饭,当然机智的 ZGY 一定会走最短路 学校的路可以看成一无向联通张图,图上有 n 个点,m 条边,每一个点都有一个唯一的编号 1~n 每一条边有一个边权,表示两个点之间的距离,ZGY 的宿舍在 S 点,机房在 T点,而小卖部在 H 点 现在 ZGY 想知道从宿舍经过小卖部到达机房的最短距离 不过因为在这个世界上有 Q个 ZGY,所以你必须回答 Q 个问题 很棒的数据分治题 读入里面说了会读入测试点编号-- 其实是很明显的暗示了-- 一半

机房测试3:ZGY的早餐(Floyd+倍增)

题目: 分析: 由数据范围可知:前五个点是Floyd,后五个点是一颗树,两两点之间的路径是唯一的,只需要求lca即可. Floyd注意实现细节: 1.初始化时要把dis[i][i]赋成0 2.只有1个dis数组 倍增注意: 统计答案的时候要先统计在跳fa!! #include<bits/stdc++.h> using namespace std; #define ll long long #define N 1005 #define nn 100005 #define M 250005 #de

机房测试9.23

题解之前 今天还行啊. Set 肯定要取模. 开始还在想vector这种操作,后来一个dalao发现一定有解,然后有发现一定有一种答案是连续的一段区间,于是就切掉了. 看了题解才发现我们只是运气好. 前缀和如果有n取值,就选%n=0的那一个,不然至多只剩n-1个取值,然而又有n个前缀和. 所以必然有两个相等,输出这之间的下标即可. #include<cstdio> #include<cctype> #include<cstring> #define FN "s

机房测试10.6

砖块 很简单的水题,写起来也很简单. 模拟写的很短,是否也是码力的体现? #include<cstdio> #include<cstring> #include<map> #define FN "block" int mp[1105][1105],dao,ans; char s[105]; int x,y,dx[4]={0,0,-1,1},dy[4]={1,-1,0,0}; std::map<char,int> m; void init(

[机房测试]数字谜题

小 X 同学有很强的计算能力,现在他正在玩一个游戏. 现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的个数 小 X 不断的进行操作,直到这个数变成 1 为止 由于小 X 的计算能力很强,他现在给出一 n 他想知道有多少不超过 n 的正整数会在 k 次操作后变成 1 由于答案可能很大,请对 1000000007 取模 因为数据范围是\(2^{1000}\),所以一次操作后最多就只有1000个1了 因此直接暴力处理出1~1000所有数变为1的操作次数,把次数为 \(k-1\

机房测试1:string(线段树)

题目: 分析: 暴力:每一次对区间暴力排序. 优化:如果可以知道一个区间中有哪种字符,这些字符分别有多少个,就可以直接按字典序枚举,将它们快速地插入区间中了. 题中有一个重要信息:只有小写字母,即只有26种字符. 第一种方法: 可以用一个线段树来维护,每个节点储存26个字符在这个区间中的对应情况.每次修改,就query统计出所求区间每一种字符的个数. 然后再区间修改,具体见代码. #include<bits/stdc++.h> using namespace std; #define mid

[20191003机房测试] 太阳神

太阳神拉很喜欢最小公倍数,有一天他想到了一个关于最小公倍数的题目 求满足如下条件的数对(a,b)对数: a,b 均为正整数且 a,b<=n 而lcm(a,b)>n 其中的 lcm 当然表示最小公倍数 答案对 1,000,000,007取模 数据范围是1e10的,打表找了半天规律发现没用-- 那就莫比乌斯反演呗 题目求: \[\sum_{i=1}^{n}\sum_{j=1}^{n}[lcm(i,j)> n]\] 但是大于的太多了,那我们就反过来求,最后用总的来减 也就是求: \[\sum_