【题解】Atcoder AGC#16 E-Poor Turkeys

  %拜!颜神怒A此题,像我这样的渣渣只能看看题解度日╭(╯^╰)╮在这里把两种做法都记录一下吧~

  题解做法:可以考虑单独的一只鸡 u 能否存活。首先我们将 u 加入到集合S。然后我们按照时间倒序往回推,如果在时间 t 的时候发现有 u 和 v 同时被抉择,为了保证 u 的存活我们只能杀掉 v,也就是说在 t - 1的时刻 v 必须存活。这时我们将 v 加入到集合 S 中,再继续进行这个过程。如果在某个时刻我们发现 u 和 v 同时被抉择,可 u 和 v 都已经在集合中出现过了(要求在这个时刻一并存活),这样显然是非法的。所以可以判定 u 没有存活的可能。

  如果一只鸡 u 能够存活,我们把这个过程中获得的 S 集合称作 \(S_{u}\) 。u 和 v 能够共存的充要条件即为 u 和 v 均有存活的可能,且 \(S_{u}\) 和 \(S_{v}\) 两个集合不存在交集。为什么呢?因为一只鸡在 t 时刻出现在了 S 集合中,说明它将在 t 时刻被杀掉。如果两个集合中 x 出现的时间不同,那么出现了冲突;但它们又不可能在同一个时间出现,因为一个时间节点只有唯一的一个抉择,反推回去也必然都是一样的,但开始的节点一个是 u,一个是 v,所以不可能。得证。

  代码:

#include <bits/stdc++.h>
using namespace std;
#define maxn 405
#define maxm 100500
int n, m, x[maxm], y[maxm], mark[maxn];
int ans, S[maxn][maxn];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); }
    while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
    return x * k;
}

int main()
{
    n = read(), m = read();
    for(int i = 1; i <= m; i ++) x[i] = read(), y[i] = read();
    for(int i = 1; i <= n; i ++)
    {
        memset(mark, 0, sizeof(mark));
        mark[i] = 1; S[i][++ S[i][0]] = i;
        for(int j = m; j >= 1; j --)
        {
            if(mark[x[j]] && mark[y[j]]) { S[i][0] = -1; break; }
            if(mark[x[j]]) mark[y[j]] = 1;
            else if(mark[y[j]]) mark[x[j]] = 1;
        }
        if(S[i][0] == -1) continue;
        for(int j = 1; j <= n; j ++)
            if(mark[j]) S[i][0] ++, S[i][S[i][0]] = j;
    }

    for(int i = 1; i <= n; i ++)
        for(int j = i + 1; j <= n; j ++)
        {
            if(S[i][0] == -1 || S[j][0] == -1) continue;
            memset(mark, 0, sizeof(mark)); bool flag = 1;
            for(int k = 1; k <= S[i][0]; k ++) mark[S[i][k]] = 1;
            for(int k = 1; k <= S[j][0]; k ++)
                if(mark[S[j][k]]) { flag = 0; break; }
            if(flag) ans ++;
        }
    printf("%d\n", ans);
    return 0;
}

  下面是颜神的解法(并没有代码...)也非常的妙,而且复杂度比题解还低……可以考虑建出一张图,在这张图上面所有有连边的鸡均无法共存来获得答案。那么如何建出这张图?一个人选择了 u 和 v 这两只鸡,那么这两只鸡是一定不可能共存的。假设我们在 t - 1 时刻建出的图满足在 t - 1 时刻及之前出现的所有抉择所限制不能共存的鸡均有连边,那么考虑加入t时刻的抉择之后会对这张图产生什么影响。

  考虑 u 和 v 的抉择,会使哪些原本可以和 u 共存的鸡不能再和 u 共存?如果图中的一只鸡 x 与 u 没有连边,也与 v 没有连边,那么它与 u 的生死无关;如果一只鸡 x 与 v 有连边,而与 u 没有连边,说明 u 和 x 不能共存,我们添加一条从 u 到 x 的边。因为 x 与 v 不能共存,所以若 x 存活,v 一定死亡。那么新的 u,v 边一定会导致 u 的死亡;若 x 死亡,那么 u 可能依然存活,也可能已经死亡;但在这两种情况下,x 都不能与 u 共存。对于 v 我们也是一样的添边。

  最后检查一下哪些节点是可以共存的即可。

原文地址:https://www.cnblogs.com/twilight-sx/p/9901626.html

时间: 2024-11-09 09:47:12

【题解】Atcoder AGC#16 E-Poor Turkeys的相关文章

题解 [Atcoder ABC 161] A,B,C

题解 [Atcoder ABC 161] A,B,C A: 水题,按题意模拟即可. code: #include<bits/stdc++.h> #define ft(i,l,r) for(register int i=l;i<=r;i++) #define fd(i,r,l) for(register int i=r;i>=l;i--) using namespace std; int a,b,c; int main() { cin>>a>>b>>

Atcoder AGC016 E Poor Turkeys

比赛的时候口胡这道题口胡了一年,看完题解被教做人 题意:有n只火鸡,m个猎人按序来杀火鸡,从自己预先选的两只中杀一只,问有多少火鸡对可以同时存活 考虑对于每一只火鸡i,按时间逆序维护一个最小的集合Si,满足当前时间其中的所有火鸡都活着才能保证最后火鸡i活下 在当前操作的最前面加入新的操作x y对结果转移的影响 1.x y均不在集合中,显然与i的死活无关,不管 2.一个在集合中,不放设为x,则y在这个操作前必须活着才能保证这个操作后x活着 3.都在集合中,gg 从最后一步逆推到第一步,得到集合Si

AtCoder Grand Contest 016 E - Poor Turkeys

题目传送门:https://agc016.contest.atcoder.jp/tasks/agc016_e 题目大意: 有\(N\)只火鸡,现有\(M\)个人,每个人指定了两只火鸡\(x,y\),每人依次进行操作,会从\(x,y\)中选一只火鸡吃掉:如果只有一个,那么必定吃掉剩下那个:如果都没有,这个人只能饿着肚子离开了-- 问最后有多少对火鸡可能存活 考虑倒序,我们设状态\(f_{i,j}\)表示如果要留下\(i\),那么是否要炖了\(j\),初始状态\(f_{i,i}=1\) 我们倒序考虑

[agc016E]Poor Turkeys

Description 传送门 Solution 如果真的按照题目要求一对对的考虑好麻烦的..(我场上就没嗑出来) 我们定义集合ai,假如鸡i要存活,则它需要哪一些鸡在它和i相关的鸡同时被拎出来前存活. 可能定义看得人有点晕..是这样,初始时ai里只有i.则我们要求所有和i同时被拎出来过的鸡,在和i同时被拎出来前存活,将这些点加到ai里. 然后ai里的每一个点都按上述方式扩展直到无法扩展,实现用dfs就好.(假如在扩展中某一个节点已经在集合a中的时候又被访问到则鸡i一定会被吃,证明显然) 我们枚

【AGC016E】Poor Turkeys

Description 有\(n\)(\(1 \le n \le 400\))只鸡,接下来按顺序进行\(m\)(\(1 \le m \le 10^5\))次操作.每次操作涉及两只鸡,如果都存在则随意拿走一只:如果只有一只存在,拿走这一只:如果都不存在,什么都不做. 求最后有多少对鸡(无序)可能共同存活. Solution 个人认为单用集合的解释方法有失偏颇. 首先考虑枚举两只鸡,规定它们一定要存活,然后模拟过程.怎么看单次模拟的复杂度都不会小于\(m\),因此要第一时间舍弃这种方法. 于是要换个

AtCoder AGC #3 Virtual Participation

Havana真好听qwq AB题就不写了 SB C.BBuBBBlesort! 有一个长度为$n$的数列 你每次可以用两种操作 1.交换两个相邻元素 2.交换两个隔且仅隔了一个的元素 求把数列排成有序的,最少需要多少1操作 sol:显然,2操作并不会改变排序后元素所处位置的奇偶性 我们找到所有排序后位置与现在位置差为奇数的点,再除以2就可以了 D.Anticube 给出$n$个两两不同的数,选出一些数使得不存在两个数的积是完全立方数,求能选出的最大数量 sol:套路 把每个数分解质因数,求出他们

题解-AtCoder ARC-078F Mole and Abandoned Mine

problem ATC-arc078F 题意概要:给定一个 \(n\) 点 \(m\) 边简单无向图(无自环无重边),边有费用,现切去若干条边,使得从 \(1\) 到 \(n\) 有且仅有一条简单路径,求最小化花费. \(n\le 15, n-1\le m\le \binom n2\) Solution 看到 \(n\leq 15\) 大概就能猜到复杂度是 \(O(3^n)\) 左右的,然后直接思考用斯坦纳树咋解,无果. 开始思考最终局面的情况,一定是有一条 \(1\) 到 \(n\) 的路径,

题解 AtCoder abc154 F

题目大意 令 \(f(r,c)=C_{r+c}^r\),要求输出 \(\sum_{i=r_1}^{r_2}\sum_{j=c_1}^{c_2} f(i,j)\),其中 \(r_2,r_2,c_1,c_2\) 为给定值. 分析 令 \[g(r,c)=\sum_{i=0}^r\sum_{j=0}^c f(i,j)\] 则题目所求为 \[\sum_{i=r_1}^{r_2}\sum_{j=c_1}^{c_2} f(i,j)=g(r_2,c_2)-g(r_2,c_1-1)-g(r_1-1,c_2)+g(

【AtCoder】AGC016

A - Shrinking 用每个字母模拟一下就行 #include <bits/stdc++.h> #define fi first #define se second #define pii pair<int,int> #define mp make_pair #define pb push_back #define space putchar(' ') #define enter putchar('\n') #define eps 1e-10 #define MAXN 200