【HDU - 5927】 Auxiliary Set 【DFS +思维】

本文介绍了一种特殊的栈数据结构及其操作,包括PUSH、POP、REVERSE和QUERY等,其中QUERY操作需要进行NAND逻辑运算并返回结果。文章还提供了一个C++实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mr. Frog learned a basic data structure recently, which is called stack.There are some basic operations of stack:

∙∙ PUSH x: put x on the top of the stack, x must be 0 or 1.
∙∙ POP: throw the element which is on the top of the stack.

Since it is too simple for Mr. Frog, a famous mathematician who can prove “Five points coexist with a circle” easily, he comes up with some exciting operations:

∙∙REVERSE: Just reverse the stack, the bottom element becomes the top element of the stack, and the element just above the bottom element becomes the element just below the top elements… and so on.
∙∙QUERY: Print the value which is obtained with such way: Take the element from top to bottom, then do NAND operation one by one from left to right, i.e. If atop,atop−1,⋯,a1atop,atop−1,⋯,a1 is corresponding to the element of the Stack from top to the bottom, value=atopvalue=atop nand atop−1atop−1 nand … nand a1a1. Note that the Stack will not change after QUERY operation. Specially, if the Stack is empty now,you need to print ” Invalid.”(without quotes).

By the way, NAND is a basic binary operation:

∙∙ 0 nand 0 = 1
∙∙ 0 nand 1 = 1
∙∙ 1 nand 0 = 1
∙∙ 1 nand 1 = 0

Because Mr. Frog needs to do some tiny contributions now, you should help him finish this data structure: print the answer to each QUERY, or tell him that is invalid.
Input
The first line contains only one integer T (T≤20T≤20), which indicates the number of test cases.

For each test case, the first line contains only one integers N (2≤N≤2000002≤N≤200000), indicating the number of operations.

In the following N lines, the i-th line contains one of these operations below:

∙∙ PUSH x (x must be 0 or 1)
∙∙ POP
∙∙ REVERSE
∙∙ QUERY

It is guaranteed that the current stack will not be empty while doing POP operation.
Output
For each test case, first output one line “Case #x:w, where x is the case number (starting from 1). Then several lines follow, i-th line contains an integer indicating the answer to the i-th QUERY operation. Specially, if the i-th QUERY is invalid, just print ” Invalid.”(without quotes). (Please see the sample for more details.)
Sample Input
2
8
PUSH 1
QUERY
PUSH 0
REVERSE
QUERY
POP
POP
QUERY
3
PUSH 0
REVERSE
QUERY
Sample Output
Case #1:
1
1
Invalid.
Case #2:
0

题意 : T组数据(T<=1000),对于每组数据,N(N<=100000)个点的一棵树,根节点为1,一个点在Set里需要满足下列情况之一:
    1.这个点是特殊点  2.这个点是两个特殊点的最近公共祖先(LCA)。
  M(M<=100000)个询问,每次询问给一个Q(Q<=N),表示N个点里面有Q个点不是特殊点,接下来是Q个点。求Set里有几个数。
  ∑Q<=100000,超过1000的N或∑Q的数据组数<=10
分析 : 想到了思路但是不知道怎么缩短时间复杂度,思路就是 只要以不重要的点为根的子树大于等于2个,并且至少有两个子树中要有重要点,那么当前的不重要点就可以添加到Set中。
看了题解, 发现dfs一遍就可以了 。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;
#define first fi
#define second se
#define  LL long long
#define fread() freopen("in.txt","r",stdin)
#define fwrite() freopen("out.txt","w",stdout)
#define CLOSE() ios_base::sync_with_stdio(false)

const int MAXN = 1e5+11;
const int MAXM = 1e6;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;

int n,q;
vector<int>ve[MAXN];int root=1;
void init(){
    for(int i=0;i<=n;i++) ve[i].clear();
}
int fa[MAXN],dep[MAXN],son[MAXN];
void dfs(int now,int pre,int de){
    fa[now]=pre; dep[now]=de; son[now]=0;
    for(int i=0;i<ve[now].size();i++){
        int v=ve[now][i];
        if(v==pre) continue;
        dfs(v,now,de+1);
        son[now]++; // 得到直系的子节点数目,不算上重子节点
    }
}

int cnt[MAXN]; int m[MAXN];
bool cmp(int a,int b){  // 每次询问按照 dep 由小到大排序
    return dep[a]>dep[b];
}
int main(){
    CLOSE();
//  fread();
//  fwrite();
    int T;scanf("%d",&T);int ncase=1;
    while(T--){
        printf("Case #%d:\n",ncase++);
        init();
        scanf("%d%d",&n,&q);
        for(int i=1;i<n;i++){
            int a,b;scanf("%d%d",&a,&b);
            ve[a].push_back(b);
            ve[b].push_back(a);
        }
        dfs(root,0,0);
    //  for(int i=1;i<=n;i++) printf("%d ",son[i]); puts("");
    //  for(int i=1;i<=n;i++) printf("%d ",dep[i]); puts("");
        while(q--){
            int mi;scanf("%d",&mi);
            for(int i=1;i<=mi;i++){
                scanf("%d",&m[i]);
                cnt[m[i]]=son[m[i]];// 得到每个不重要节点的直系子节点数目
            }
            sort(m+1,m+1+mi,cmp);//按照 深度由大到小排序
            int ans=0;
        //  for(int i=1;i<=mi;i++) printf("%d ",m[i]);  
            for(int i=1;i<=mi;i++){
                if(cnt[m[i]]>=2)  ans++; // 如果子树大于等于两个,说明每个子树都可以提供一个重要点,所以这个点肯定可以为Set中点,所以加一
                else if(cnt[m[i]]==0)  cnt[fa[m[i]]]--; // 为零的话,表明其父节点有一个子树是无法提供 重要点的 ,删去。
             } 
             printf("%d\n",ans+n-mi);
        } 
    } 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值