18.9.22 考试总结

这道题一看就是可持久化并查集 然后我就愉快的yy了一波 还是错掉了qwqwqwqwq

方法是对的 就是我每次在树上查询$fa$的时候我还压缩了路径 导致这玩意空间炸掉了

所以要保证时间复杂度 就启发式合并 也就是$size$小的往$size$大的搞

这样子就保证每次合并的时候连通块元素个数每次至少乘以$2$ 也就是保证了层数是$log$级的

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n,f[90 * N],ls[90 * N],rs[90 * N],size[90 * N],root[N];
int cnt,m,fa[N];

int build(int o, int l ,int r) {

    int nd = ++ cnt;
    if(l == r) {
        f[nd] = l; size[nd] = 1;
        return nd;
    }
    int mid = (l + r) >> 1;
    ls[nd] = build(2 * o, l, mid);
    rs[nd] = build(2 * o + 1, mid + 1, r);
    return nd;
}

void Init( ) {

    scanf("%d%d",& n,& m);
    root[0] = build(1, 1, n);
}

int modify_fa(int pre, int o, int l, int r, int pos, int del) {

    int nd = ++ cnt;
    f[nd] = f[pre],ls[nd] = ls[pre],rs[nd] = rs[pre],size[nd] = size[pre];
    if(l == r) {
        f[nd] = del; return nd;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) {
        ls[nd] = modify_fa(ls[pre], 2 * o, l, mid, pos, del);
    }
    else rs[nd] = modify_fa(rs[pre], 2 * o + 1, mid + 1, r, pos, del);
    return nd;
}

int modify_size(int pre, int o, int l, int r, int pos, int del) {

    int nd = ++ cnt;
    f[nd] = f[pre],ls[nd] = ls[pre],rs[nd] = rs[pre],size[nd] = size[pre] + del;
    if(l == r) {
        return nd;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) {
        ls[nd] = modify_size(ls[pre], 2 * o, l, mid, pos, del);
    }
    else rs[nd] = modify_size(rs[pre], 2 * o + 1, mid + 1, r, pos, del);
    return nd;
}

int query_fa(int nd, int o, int l, int r, int pos) {

    if(l == r) {
        return f[nd];
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) return query_fa(ls[nd], 2 * o, l, mid, pos);
    else return query_fa(rs[nd], 2 * o + 1, mid + 1, r, pos);
}

int query_size(int nd, int o, int l, int r, int pos) {

    if(l == r) {
        return size[nd];
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) return query_size(ls[nd], 2 * o, l, mid, pos);
    else return query_size(rs[nd], 2 * o + 1, mid + 1, r, pos);
}

int Find_fa(int x, int M) {

    int ff = query_fa(root[x], 1, 1, n, M);
    if(ff == M) return ff;
    int F = Find_fa(x, ff);
    return F;
}

void Solve( ) {

    int las = 0;
    for(int i = 1;i <= m;i ++)
     {
        int opt,x,y;
        scanf("%d",& opt);
        if(opt == 1) {
            scanf("%d%d",& x,& y); x = x + las, y = y + las;
            int fa1 = Find_fa(i - 1, x), fa2 = Find_fa(i - 1, y);
            if(fa1 == fa2) {
                root[i] = root[i - 1]; continue;
            }
            int s1 = query_size(root[i - 1], 1, 1, n, fa1);
            int s2 = query_size(root[i - 1], 1, 1, n, fa2);
            if(s1 < s2) { swap(s1,s2); swap(fa1, fa2); }
            root[i] = modify_fa(root[i - 1], 1, 1, n, fa2, fa1);
            root[i] = modify_size(root[i], 1, 1, n, fa1, s2);
        }
        else {
            scanf("%d%d",& x,& y); x = x + las, y = y + las;
            root[i] = root[i - 1];
            int fat = Find_fa(x, y);
            int s = query_size(root[x], 1, 1, n, fat);
            printf("%d\n",s); las = s;
        }
    }
}

int main( ) {

    freopen("build.in","r",stdin);
    freopen("build.out","w",stdout);
    Init( );
    Solve( );
}

这道题班上有人用贪心A掉了

而蒟蒻我只会垃圾dp  qwqwq 首先我们可以发现这个玩意正着来搞是不行的

因为每个人一旦选择这座城市自己要 那么他的下一个人的选择是确定的

并且后面的选择跟前面的毫无联系 就无法体现最优选择的思想 所以我们考虑倒着搞 维护一个$sum$前缀和

$dp[i][0/1][0/1]$表示到了第$i$个城市 当前是谁的回合 这个人选不选择这个城市 这个人获得的最大收益

$dp[i][now][1] = sum[n] - sum[i] + a[i] - max(dp[i +1][now xor 1][0],dp[i + 1][now xor 1][1])$

表示如果我选择这座城市 相当于把主动权给别人 那么下个人一定会选择它的最优方案 总的收益是一定的 所以就用总收益减去别人的收益就可以了

$dp[i][now][0] = max(dp[i + 1][now][1],dp[i + 1][now][0])$

表示如果我这个位置不选 我的最大收益就是后面的选择所带来的最大收益

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
int n;
ll dp[N][2][2],sum[N],a[N];

void Init( ) {

    scanf("%d",& n);
    for(int i = 1;i <= n;i ++) {
        scanf("%lld",& a[i]);
        sum[i] = sum[i - 1] + a[i];
    }
}

void Solve( ) {

    dp[n][1][1] = a[n], dp[n][0][1] = a[n];
    for(int i = n - 1;i >= 1;i --) {
        for(int now = 0;now <= 1;now ++) {
            dp[i][now][1] = a[i] + sum[n] - sum[i] - max(dp[i + 1][now ^ 1][0], dp[i + 1][now ^ 1][1]);
            dp[i][now][0] = max(dp[i + 1][now][1], dp[i + 1][now][0]);
        }
    }
    printf("%lld\n",max(dp[1][1][1], dp[1][1][0]));
}

int main( ) {

    freopen("distribute.in","r",stdin);
    freopen("distribute.out","w",stdout);
    Init( );
    Solve( );
}

然后我考试的时候做不来这道题

emmmmmm考虑要存在合法的圆边那么他肯定存在于一个环里面 在无向图里面 他肯定是一个点双连通分量

所以把每个点双处理出来 如果边数等于点数 他就是一个合法简单环 否则会有不止一个环 就不合法

考虑为什么是点双连通 不是边双呢 因为只有点双才是多个简单环叠加

左边这个虽然是边双 但是他是合法的 若是按照之前的方法 他会被判断成不合法 因为他不是简单环的叠加 而点双就一定是叠加 判断就一定合法

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n,head[N],nex[2 * N],tov[2 * N],id[2 * N],c[N];
int dfn[N],low[N],idc,tot = 1,m,stk[2 * N],top,col,que[2 * N];
bool vis[2 * N],isans[2 * N];

void add(int u, int v, int ID) {

    tot ++;
    nex[tot] = head[u];
    tov[tot] = v; id[tot] = ID;
    head[u] = tot;
}

void Init( ) {

    scanf("%d%d",& n,& m);
    for(int i = 1;i <= m;i ++) {
        int u,v;
        scanf("%d%d",& u,& v);
        add(u, v, i); add(v, u, i);
    }
}

void tarjan(int u,int from) {

    low[u] = dfn[u] = ++idc;
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if((i ^ 1) == from) continue;
        if(! vis[i]) vis[i] = vis[i ^ 1] = true, stk[++ top] = i;
        if(! dfn[v]) {
            tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]) {
                col ++;
                int cntn = 0,cnte = 0;
                while(1) {
                    int e = stk[top --];
                    if(c[tov[e]] != col) c[tov[e]] = col,cntn ++;
                    if(c[tov[e ^ 1]] != col) c[tov[e ^ 1]] = col, cntn ++;
                    que[++ cnte] = e;
                    if(e == i) break;
                }
                if(cnte == cntn) {
                    for(int i = 1;i <= cnte;i ++)
                        isans[que[i]] = isans[que[i] ^ 1] = true;
                }
            }
        }
        else if(low[u] > dfn[v]) low[u] = min(low[u], dfn[v]);
    }
}

void Solve( ) {

    for(int i = 1;i <= n;i ++) {
        if(! dfn[i]) tarjan(i, 0);
    }
    int ans = 0;
    for(int i = 2;i <= tot;i += 2) if(isans[i]) ans ++;
    printf("%d\n",ans);
    for(int i = 2;i <= tot;i += 2)
        if(isans[i]) printf("%d ",i / 2);
}

int main( ) {

    freopen("find.in","r",stdin);
    freopen("find.out","w",stdout);
    Init( );
    Solve( );
}

原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9690596.html

时间: 2024-08-29 17:53:15

18.9.22 考试总结的相关文章

18.10.22 考试总结

我真的我要被我自己给猪死了md T1让输出边长我输出面积硬生生掉了100分 气死 这道题我之前在讲单调栈的时候是讲过的 对于每一个位置 维护一个他的$up$表示以这里为起点只走$1$向上走的高度 然后对于每一行都跑一边求最大矩形的单调栈即可 维护一个单增的 每次弹栈的时候求出以弹栈元素为矩形边长的最大矩形面积 不过这道题是求最大正方形 就改成边长取$min$再取$max$即可 代码 #include <bits/stdc++.h> #define il inline #define rg re

9.22考试总结

9.22考试总结 购物 这是一道可以DP可以贪心的题目.由于我DP不是很好一点不懂所以改题就没有用DP 因为考试的时候还是对这道贪心的题目思考的比较复杂.虽然最开始以为是一道邮票面值问题,但是到后面还是发现了它要用一定数目的邮票凑齐想要的面额 思路大概是先用小面值的钱凑出金额.如果当前凑出来的金额有同样大小的面值可以替换就加一张 可以凑出的面值为1~s的钱,我们想要去凑出面值为s+1的情况,所以说我们需要寻找一张面值为s+1的,如果没有,我们的选择将会是最大面值的,再用它(这个面值大的),去和其

9.22考试 crf的数数 题解

这道题当时第一反应是线段树,但没有继续想,因为当时打完第一题打算这道题和第二道题并列做,打完第二道题状压后时间还有两个小时多,先打完暴力再说,打完之后又接着去想,然后想了5分多钟吧,扑街. 然后就发现这题似曾相识,有点像指针恒给我讲的分块"数颜色",于是如获至宝的打了一个标准的分块.然后满心期待的以为至少能拿60分以上,结果被丝薄数据卡的一分没得,好无良的出题人啊. 考完试后发现好多人拿莫队打了70分,才反应过来这道题可以拿莫队打,然而之前只是听Q某犇讲过莫队的原理,具体实现也没打过,

9.22考试 crf的视察 题解

这道题当时第一反应就是一道典型的NOIP第一题的难度,绝对要A掉,不然分数一定会被拉开. 然后就开始分析,暴力是一开始想的是用二维树状数组打加上暴力美剧长度,然而这道题满足二分性质,所以时间复杂度就是log n^3*n^2,还是会T然后就发现完全可以不用树状数组,直接n^2预处理统计起来然后log n* n^2二分答案并验证就可以了.大概从考试开始到打完不到17分钟吧,个人感觉还是可以的. 1 #include<iostream> 2 #include<cstdlib> 3 #in

lazarus 2016 2月18 4:22:35 支持android开发了, 既ios,linux,macosx,window,web 后 囊括一切啦。 哈哈

Android Development Lazarus for Linux Lazarus for Mac OS X Lazarus for iOS Lazarus for Windows Lazarus for Web Lazarus 1.6 - Released - February 18, 2016, 04:22:35 pm

9.22考试 crf的军训 题解

做这道题时由于第一道题太水了,第一反应是NOIP T2级别的题,需要拿上70~100的分,然后就开始分析,当然最后事实证明我错了-- 这道题当时首先联想到了 NOIP2016愤怒的小鸟 当然,数据范围不允许,但是我当时只是为了先拿到小数据的分数,所以先没考虑数据范围,在这里简单提一下:首先我先枚举了每一个状态,然后判断这个状态中的书是否能连在一起,然后就是一个2^(2*n)的转移,好吧,我承认,不是正宗的愤怒的小鸟打法,是当时集中生智编出来的,但是对于n<=10的复杂度还是够用的. 然后对于小于

18.1.1考试

今天考的题目都是DP,本来以为会有什么图论...根据今天题目比较水的原因,我直接放解题报告,大家应该可以看得懂.. T1 Source #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> using namespace std; const int maxn=10000+10,maxm=10010; #define file(a) freopen(a".i

18.8.13 考试总结

1.1 问题描述请构造一颗n 个节点的树,使得其价值最大.f(d) 表示树上,度数为d 的一个点能够获取的价值.这棵树的价值为Σni=1 f(di)di 表示第i 个点的度数1.2 输入第一行一个整数T,接下来T 组数据,每组数据输入两行.第一行输入整数n.第二行输入n ?? 1 个整数f(i) 代表f(1) f(n ?? 1).1.3 输出对于每组数据输出一行,为能够构造的树的最大价值. 一开始我以为是一道树形dp... 考完了才知道原来这个是一道背包问题 n个节点 总共有2n - 2个度数

18.8.26 考试总结

我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了20分.. 这道题是一道找规律的题 看完题很显然能够发现我们可以将相同颜色的连通块缩点 因为同一个联通块的可以一次操作全部变成另外一种颜色 所以就缩点就好了.. 对于缩点后的一条链 每次我们可以将一个点变色 那么和他相邻的点就和他颜色一样 然后就再次缩点 所以每次链的长度都可以 -2 所以说最后缩点的次数就是  点的个数 / 2  (下取整) 但是这是对于一条链 那么对于一棵树而言呢? 其实是一样的 因为每次搞这个操作 我们都可