情之所至,甘之如饴

题目传送门

解题思路

这道题操作简单,没多想就打了个暴力上去,结果11个点AC了10个,90分。

无奈之下,翻了翻题解,看到一个巧妙的离线并查集解决方法,于是就看明白之后学来了。

首先读入每一个操作,cnt数组记录每个点被标记的次数。如果一个点被标记了,cnt[i]++;

对于询问节点x最近的被打了标记的祖先,只需考察cnt数组即可。如果cnt[x]!=0,那么答案就是x自己,否则进行并查集的find操作,找到符合条件的祖先。逆序操作每一个标记和询问(并查集一类经典的解题思路,相似的题目有JSOI2008 星球大战等),对于标记操作,只需按照上文方法维护cnt数组即可。对于询问操作,使用并查集解决。

这种方法真是从来没听到过呢,也没有人讲过。但毕竟是OI,没点自主学习的能力,怎么能算一个合格的OIer呢???

贴下我优美的拿指针邻接表实现的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define R register
#define I inline
template<class T>void read(T &x) {
register ll c = getchar(), f = 1; x = 0;
while(!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
x *= f;
}
const int maxn=300030;
struct edge {
int v;
edge *next;
}*h[maxn],pool[maxn];
int ans[maxn],fa[maxn],father[maxn],cnt[maxn];
int n,q;
int tot;
struct data {
int type,num;
}a[maxn];
void addEdge(int u,int v)
{
edge *p=&pool[++tot];
p->v=v;p->next=h[u];h[u]=p;
}
int find(int x)
{
return father[x]==x?x:father[x]=find(father[x]);
}
void init(int u,int tp)
{
fa[u]=tp;
for(edge *p=h[u];p;p=p->next)
{
int v=p->v;
if(v==fa[u]) continue;
init(v,u);
}
}
int main()
{
read(n);read(q);
for(int i=2;i<=n;i++)
{
int from,to;
read(from);read(to);
addEdge(from,to);
addEdge(to,from);
}
for(int i=1;i<=q;i++)
{
char op[10];
int x;
scanf("%s",op);
read(x);
if(op[0]=='C') {
cnt[x]++;
a[i].num=x;
a[i].type=0;
}
else {
a[i].num=x;
a[i].type=1;
}
}
init(1,1);
for(int i=1;i<=n;i++) {
if(cnt[i]) father[i]=i;
else father[i]=fa[i];
}
for(int i=q;i>=1;i--)
{
if(a[i].type) ans[i]=find(a[i].num);
else {
cnt[a[i].num]--;
if(!cnt[a[i].num])
father[a[i].num]=fa[a[i].num];
}
}
for(int i=1;i<=q;i++)
if(ans[i])
printf("%d\n",ans[i]);
return 0;
}

题目传送门

解题思路

建图,然后记忆化搜索。对于某个点记录入度和出度,注意单独的一种生物不算做一条食物链。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define R register
#define I inline
template<class T>void read(T &x) {
register ll c = getchar(), f = 1; x = 0;
while(!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
x *= f;
}
const int maxn=100100,maxm=200200;
int dp[maxn],ind[maxn],outd[maxn];
struct edge {
int v,c;
edge *next;
}*h[maxn],pool[maxm];
int tot;
void addEdge(int u,int v)
{
edge *p=&pool[++tot];
p->v=v;p->next=h[u];h[u]=p;
ind[v]++;outd[u]++;
}
void dfs(int u)
{
if(dp[u]) return ;
if(outd[u]==0) {
dp[u]=1;
return ;
}
for(edge *p=h[u];p;p=p->next)
{
int v=p->v;
dfs(v);
dp[u]+=dp[v];
}
}
int n,m,ans;
int main()
{
read(n);read(m);
while(m--)
{
int u,v;
read(u);read(v);
addEdge(u,v);
}
for(int i=1;i<=n;i++)
if(!ind[i] && outd[i]) dfs(i),ans+=dp[i];
printf("%d\n",ans);
return 0;
}



本站使用 Material-X 作为主题 , 总访问量为 次 。
载入天数...载入时分秒... 字数统计:726.4k