【AGC016E】Poor Turkeys

Description

  
  有\(n\)(\(1 \le n \le 400\))只鸡,接下来按顺序进行\(m\)(\(1 \le m \le 10^5\))次操作。每次操作涉及两只鸡,如果都存在则随意拿走一只;如果只有一只存在,拿走这一只;如果都不存在,什么都不做。
  
  求最后有多少对鸡(无序)可能共同存活。
  
  
  

Solution

  
  个人认为单用集合的解释方法有失偏颇。
  
  首先考虑枚举两只鸡,规定它们一定要存活,然后模拟过程。怎么看单次模拟的复杂度都不会小于\(m\),因此要第一时间舍弃这种方法。
  
  于是要换个角度考虑。我们看看能不能算出某一只鸡存活的条件,再枚举两只鸡,并判断它们的条件是否冲突。
  
  假设我们令\(a\)必须存活。
  
  先看那些与\(a\)有关的操作:显然,另一只鸡在该操作前必须存活。我们虽然得到了这个结论,但是这些操作的顺序有先后影响,并不好考虑。
  
  为了消除后效性,我们从后往前考虑每个事件。如果遇到与\(a\)有关的事件\((a,b)\),我们必须令\(b\)在这个时刻前存活。这意味着下次遇到与\(a\)或\(b\)有关的事件,我们必须令另一者在这个时刻前存活。我们记如果"\(a\)必须存活",当前所有必须存活的鸡组成的集合为\(S\),则形式化地讲:
  
  初始时,\(S\)里只有\(a\)
  
  1.如果遇到一个事件,其中一者属于\(S\),则另一者必须在这个时刻前存活。我们将另一者加进\(S\)
  
  2.如果两者都属于\(S\),则必须死一个。这立刻违反了\(S\)的定义,因此\(a\)不可能存活。我们将其纳入统计答案的考虑对象
  
  3.如果两者都不属于\(S\),由于我们从后往前考虑,即使这两者在更早的时间与\(a\)的生死有关,但那个有关的时刻结束之后,这两者的生死并不重要。因此这个事件不需要纳入考虑范围。
  
  由此,扫完全部事件之后,依赖存活关系可以形象为一棵内向树(上述1.发生时,从另一者向属于\(S\)的一者连一条有向边),我们不再将其看做集合考虑,因为那无法解释接下来的事情。我们称它为\(a\)的存活树。
  
  \(a\)的存活树的每一条边都代表着一次依赖事件,每一次事件的成功与否都决定了\(a\)能否存活。事件发生的具体顺序我们不需要知道,但是一定是按照从叶子节点向上的某个拓扑序发生的。
  
  考虑两只鸡\(a\)和\(b\)能否存活。有了存活树的概念,却无从下手?先从简单的一面看:如果二者的存活树的点集无交,那么显然没有影响,二者可以共存。关键是如果有交,可以共存吗?
  
  对于一个点\(x\),其在\(a\)和\(b\)的存活树中都出现。如果\(x\)在两棵树中的父亲不同,这代表着两次不同的事件,先后发生,却都依赖于\(x\)。则后发生的一者必然不能保证\(x\)存活,因此\(a\)和\(b\)有一个必须死。如果\(x\)在两棵树中的父亲相同,首先二者不可能是两个事件,不然二者自身都不可能存活,不在考虑范围之内;既然是同一个事件,那么它们在这一步的确共存,因为它们共同进行了有益的一步。我们会发现,两棵树中可能出现一些“共同链”,但这并不意味着二者可以共存。因为两棵树的根一定不同,所以“共同链”的链顶一定不是根,即“共同链”的链顶一定会出现第一个情况:父亲不一样,有一只鸡必须死。
  
  由上证毕,两只鸡能共存,当且仅当存活树的点集无交集。
  
  在实现时,不需要建树,树只是用来严格证明的,我们只需要计算出每只存活的鸡的存活树点集合即可。
  
    
  

Code

  

#include <cstdio>
#include <bitset>
using namespace std;
const int N=405,M=10005;
typedef bitset<N> bs400;
int n,m;
int a[M][2];
bool die[N];
bs400 b[N];
void readData(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&a[i][0],&a[i][1]);
}
void calc(){
    for(int i=1;i<=n;i++){
        b[i][i]=1;
        for(int j=m;j>=1;j--){
            int u=a[j][0],v=a[j][1];
            if(b[i][u]&&b[i][v]){
                die[i]=true;
                break;
            }
            else if(b[i][u])
                b[i][v]=1;
            else if(b[i][v])
                b[i][u]=1;
        }
    }
}
void print(){
    int ans=0;
    for(int i=1;i<n;i++)
        if(!die[i])
            for(int j=i+1;j<=n;j++)
                if(!die[j]){
                    if((b[i]&b[j]).none())
                        ans++;
                }
    printf("%d\n",ans);
}
int main(){
    readData();
    calc();
    print();
    return 0;
}

原文地址:https://www.cnblogs.com/RogerDTZ/p/9552950.html

时间: 2024-10-31 09:15:37

【AGC016E】Poor Turkeys的相关文章

【HDU4803】Poor Warehouse Keeper 数学+贪心

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43450731"); } 题意: 初始状态为:1个物品,总价为1. 目标状态为:x个物品,总价为y. 操作A:变为x+1,y+y/x.(y不取整) 操作B:变为x,  y+1 问最少多少步可以达成条件?(最后操作结束后对y取整) 如果不行输出

【HDOJ】4956 Poor Hanamichi

基本数学题一道,看错位数,当成大数减做了,而且还把方向看反了.所求为最接近l的值. 1 #include <cstdio> 2 3 int f(__int64 x) { 4 int i, sum; 5 6 i = sum = 0; 7 while (x) { 8 if (i & 1) 9 sum -= x%10; 10 else 11 sum += x%10; 12 ++i; 13 x/=10; 14 } 15 return sum; 16 } 17 18 int main() { 1

【hdu3080】01背包(容量10^7)

[题意]n个物品,有wi和vi,组成若干个联通块,只能选取一个联通块,问得到m的价值时最小要多少空间(v).n<=50,v<=10^7 [题解] 先用并查集找出各个联通块. 这题主要就是v太大了,跟以往的背包不同. 我们回想01背包,f[j+v[i]]=max(f[j]+w[i]); 在这里面很明显很多状态都没有用. 优化:如果有2个状态,v1<=v2 && w1>=w2 则(v2,w2)这个状态是没有用的. 我们回到滚动数组中: f[i][j+v[i]]=max(

HDU1242 Rescue 【BFS】

Rescue Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 16314    Accepted Submission(s): 5926 Problem Description Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is

【POJ1113】Wall(凸包)

[题目] Description Once upon a time there was a greedy King who ordered his chief Architect to build a wall around the King's castle. The King was so greedy, that he would not listen to his Architect's proposals to build a beautiful brick wall with a p

【转】Java HashMap 源码解析(好文章)

- .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wrapper iframe, .fluid-width-video-wrapper object, .fluid-width-video-wrapper embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } [

HDU1010 Tempter of the Bone 【DFS】+【剪枝】

Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 70895    Accepted Submission(s): 19535 Problem Description The doggie found a bone in an ancient maze, which fascinated him a

【故障处理】队列等待之enq IV - contention案例

[故障处理]队列等待之enq IV -  contention案例 1.1  BLOG文档结构图 1.2  前言部分 1.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 队列等待之enq IV -  contention案例(重点) Tips: ① 本文在itpub(http://blog.itpub.net/26736162).博客园(http://www.cnblogs.com/lhrbest)和微信公

红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) #include <cstdio> #define Max 100001 #define Red true #define Black false #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (i