18.9.8 考试总结

这道题就是树上贪心就好了 从下往上搞 要是遇到没有被选过得点就把他的父亲标记就好了

然后就 $bfs$ 把节点都存到一个栈里面 然后弹出的肯定是最深的点 就慢慢往上搞就可以了

代码

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

const int N = 1e5 + 5;
int stk[N],tot,top,fa[N],ans = 0;
int n,head[N],nex[2 * N],tov[2 * N];
bool vis[N],arr[N];
queue<int>Q;

void add(int u,int v) {

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

void add_Edge( ) {

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

void BFS( ) {

    Q.push(1); stk[++ top] = 1;
    vis[1] = true;
    while(! Q.empty( )) {
        int u = Q.front( ); Q.pop( );
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(vis[v]) continue;
            vis[v] = true; fa[v] = u;
            Q.push(v); stk[++ top] = v;
        }
    }
}

void Solve( ) {

    while(top) {
        if(arr[stk[top]]) {
            top --; continue;
        }
        else {
            int u = stk[top];
            ans ++;
            arr[fa[u]] = true;
            for(int i = head[fa[u]];i;i = nex[i]) {
                int v = tov[i];
                arr[v] = true;
            }
        }
        top --;
    }
    printf("%d",ans);
}

int main( ) {

    freopen("CP.in","r",stdin);
    freopen("CP.out","w",stdout);
    add_Edge( );
    BFS( );
    Solve( );
}

啊这道题很神奇 有点类似倍增 因为所有的方案总是要求一个能够覆盖所有的区间并

所以必定有一个区间是经过原点的 所以就枚举这个区间是什么 然后判断就可以了

至于判断就是 因为他是一个环 所以就常规套路 把环复制一边当作链处理

这个时候答案就是现在序列的能够从$n$ 走到$2n$的最少步数

对于数据我们可以发现有些区间是没有用处的 即那些左右端点都被别的一个区间包裹住的区间就没用

所以在跑之前就先把这些没用的区间筛去就可以了 有点类似于单调栈的思想

所以这个时候我们得到了一个左右端点均单调递增的区间序列 这时候就可以通过二分找出这个区间的$nex$是谁

$nex$就是他的连在一起的右端点能够延伸最远的区间 这样子按照贪心肯定是最优秀的

然后就是两个 $dp$ 数组了 $f[i]$表示第 $i$ 号区间跳到$2n$这个位置的步数

$g[i]$表示他最终跳到超过$2n$位置时在那个区间 有什么用呢

如果是右边的情况 就没有问题 但是如果是左边的情况 那么这个时候不用越过$2n$就可以走完了 所以答案要减一

所以判断就是他走到终点的那个区间等于他自己 $g[i]$就是拿来干这个的

至于转移还是很简单

    $f[i] = f[nex[i]] + 1$          $g[i] = g[nex[i]]$

边界就是最后那个右端点大于$2n$的  $f[i] = 1$  $g[i] = i$

然后最后就是答案就是$min(f[i])$(要满足条件)

代码

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

typedef long long ll;
const int N = 1e5 + 5;
struct data {

    ll l,r;
    data(ll l = 0,ll r = 0): l(l),r(r) { }
}a[N],q[2 * N];

int n,m,f[2 * N],g[2 * N],cnt = 0,nex[N],ans;

bool cmp(const data & a,const data & b) {

    return a.l < b.l;
}

void init( ) {

    sort(a + 1,a + m + 1,cmp);
    for(int i = m;i >= 1;i --) {
        while(q[cnt].r <= a[i].r && cnt)
            cnt --;
        q[++ cnt] = a[i];
    }
    reverse(q + 1,q + cnt + 1);
    for(int i = 1;i <= cnt;i ++)
        q[i + cnt] = q[i],q[i + cnt].l += n,q[i + cnt].r += n;
}

int find_pos(int pos) {

    int l = pos,r = 2 * cnt,ans = 1 + 2 * cnt;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(q[mid].l <= q[pos].r) ans = mid,l = mid + 1;
        else r = mid - 1;
    }
    return ans;
}

void Solve( ) {

    for(int i = 1;i <= cnt * 2;i ++) {
        nex[i] = find_pos(i);
    }
    for(int i = 2 * cnt;i >= 1;i --)
        if(q[i].r >= 2 * n) f[i] = 1,g[i] = i;
        else f[i] = f[nex[i]] + 1,g[i] = g[nex[i]];
    ans = 10000000;
    for(int i = 1;i <= cnt;i ++) {
        if(q[i].r >= n) {
            ans = min(ans,f[i] - ((g[i] - cnt) == i));
        }
    }
    printf("%d",ans);
}

int main( ) {

    freopen("gfw.in", "r", stdin);
    freopen("gfw.out", "w", stdout);
    scanf("%d%d",& n,& m);
    for(int i = 1;i <= m;i ++) {
        ll L,R;
        scanf("%lld%lld",& L,& R);
        a[i] = data(L,R + L);
    }
    init( );
    Solve( );
}

一看到这道题 我以为是他那个是noip类似的那个题 叫运输计划 就是二分答案 然后把不合法的情况 求一个重边就可以了

这个东西乍一看非常难 实际上只是一道简单的高中线性规划

这样子考虑 对于一个建立的 $x -> y$ 的传送站 一个$l -> r$ 的任务

那么他所需要花费的时间就是$|l - x| + |r - y|$  画个图就清楚了

至于终点小于$r$是很直观的 自己画画图就出来了 然后这玩意儿要小于等于枚举的最大时间p

所以打开括号就可以得到关于$x + y$ 和$x - y$的两个不等式

这个时候就求边界然后判断有没有可行解就可以了 然后边界要赋值$1e9$ 我在这上面改了半天

代码

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

typedef long long ll;
const int N = 5 * 1e5 + 5;
struct Cmd {
    ll l,r;
}a[N];
ll n,m;

bool check(ll p) {

    ll L1 = -oo,R1 = oo,L2 = -oo,R2 = oo;
    for(int i = 1;i <= m;i ++) {
        if(a[i].r - a[i].l > p) {
            L1 = max(L1,a[i].r + a[i].l - p);
            L2 = max(L2,a[i].r - a[i].l - p);
            R1 = min(R1,a[i].l + a[i].r + p);
            R2 = min(R2,a[i].r - a[i].l + p);
        }
    }
    return L1 <= R1 && L2 <= R2;
}

int main( ) {

    freopen("bricks.in","r",stdin);
    freopen("bricks.out","w",stdout);
    scanf("%lld%d",& n,& m);
    for(int i = 1;i <= m;i ++) {
        scanf("%lld%lld",& a[i].l,& a[i].r);
        if(a[i].l > a[i].r) swap(a[i].l,a[i].r);
    }
    ll l = 0,r = oo,ans = oo;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(check(mid)) ans = mid,r = mid - 1;
        else l = mid + 1;
    }
    printf("%lld",ans);
}

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

时间: 2024-10-27 16:40:48

18.9.8 考试总结的相关文章

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  (下取整) 但是这是对于一条链 那么对于一棵树而言呢? 其实是一样的 因为每次搞这个操作 我们都可

18.9.22 考试总结

这道题一看就是可持久化并查集 然后我就愉快的yy了一波 还是错掉了qwqwqwqwq 方法是对的 就是我每次在树上查询$fa$的时候我还压缩了路径 导致这玩意空间炸掉了 所以要保证时间复杂度 就启发式合并 也就是$size$小的往$size$大的搞 这样子就保证每次合并的时候连通块元素个数每次至少乘以$2$ 也就是保证了层数是$log$级的 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n

18.8.17 考试总结

高斯消元[问题描述]everlasting 觉得太无聊了,于是决定自己玩游戏! 他拿出了n 个小圆,第i 个的颜色是ai.接着他将这n 个小圆复制m 次并依次连接起来. 之后他发现其中有很多颜色相同的小圆,于是他决定:每有k 个连续颜色相同的小圆就将他们消去, 并将剩下的依次连接.(注意只会消除k个,即使有超过k 个)他将每次从头开始不断进行这个操作直到无法操作为止. 他想知道最后能剩下多少个小圆? [输入格式] 从文件guass.in 中输入数据. 第一行三个正整数n,m,k,表示开始小圆的个

18.8.19 考试总结

Gift[问题描述]人生赢家老王在网上认识了一个妹纸,然后妹纸的生日到了,为了表示自己的心意,他决定送她礼物. 可是她喜爱的东西特别多,然而他的钱数有限,因此他想知道当他花一定钱数后剩余钱数无法再购买任何一件剩余物品 (每种物品他最多买一个)时有多少种方案,两种方案不同,当且仅当两种方案中至少有一件品不 同,可是由于他忙着准备泡下一个妹纸(chi),因此麻烦聪明的你帮帮忙. [输入格式] 输入第一行 n 和m,n 表示妹纸喜欢的礼物数目,m 表示现有的钱数,第二行n 个数,表示n 个物品的价格.

18.8.31 考试总结

这道题就是打表找规律 然而我打表并没有找到规律... yyyuuudalao就找到了tqltql%%% 打表发现 $ A $ 数列是这样子1 1 ,2 ,2 ,6 1 1 2 2 3 4 4 4 5 6 6 7 8 8 8 8 9 10 10 11 12 12 12 13 14 14 15 16 16 16 16 16... $ t[i] $ 表示 $ i $ 这个数出现次数 然后发现 $t[i]$ 为 $i$ 所含的因子 $2$ 个数 $+ 1$ ($1$除外) 也就是说每个数如果多一个 $2

18.9.7 考试总结

啊今天是生日之后的第三天呢 然后我就大凶了.. 略微难受 啊这道题正解是倒着搞得 ()然而蒟蒻我并不会... 于是就学习wans大佬的程序写的二分 + 正着搞得$Spfa$ $Spfa$最坏是 $nm$ 显然这道题是可以直接跑过的 所以对于起点二分一个最大货物量 然后Spfa跑每个点的最大值就可以了 按照题目意思来稿还是很清楚的 代码 #include <bits/stdc++.h> #define oo 10000000 using namespace std; const int N =

18.10.4 考试总结

这道题就是一道肥!肠!裸!的!轮廓线dp 然后因为细节太多了还因为有一个sbsbsb编译错误 就是不准我函数名字取count...我恨 我永远讨厌轮廓线dp 代码 #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 5; int dp[2][N],n,m,len[200],cnt[N],ans; char s[200][40]; void Init( ) { scanf("%d%d",&