0%

「CF1270G」Subset with Zero Sum

题目链接

题意

给定一个长度为 nn 的序列 aa,保证 inaii1i-n\leq a_i\leq i-1

要求选出非空集合 SS,使 iSai=0\sum_{i\in S}a_i=0

思路

inaii1i-n\leq a_i\leq i-1,得 1iain1\leq i-a_i\leq n

于是对于每个点 ii,向 iaii-a_i 连一条有向边。

图上的每个环都对应一个合法的点集 SS

为什么呢?

iitoito_i 连了边,由定义得 toi=iaito_i=i-a_i

一旦 SS 形成了环,则 iSi=iStoi\sum_{i\in S}i=\sum_{i\in S}to_i

iSi=iS(iai)\sum_{i\in S}i=\sum_{i\in S}(i-a_i)

将等式右边展开得:iSi=iSiiSai\sum_{i\in S}i=\sum_{i\in S}i-\sum_{i\in S}a_i

显而易见,iSai=0\sum_{i\in S}a_i=0,符合题目要求。

实现

本来应该挺好实现的。

自己脑抽 stack 写成 queue WA 了 1 次。

数据组数 t1000000t\leq1000000,我每次清空 visvis 数组 TLE 了 3 次……

代码

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
#include<cstdio>
#include<cstring>
using namespace std;
int t,n,to[1000001];
bool flag;
class stack {
private:
int tp,s[1000001];
bool ins[1000001];
public:
void init()
{
tp=0;
return;
}
inline void push(int x)
{
ins[s[++tp]=x]=1;
return;
}
inline int top()
{
return s[tp];
}
inline void pop()
{
ins[s[tp--]]=0;
return;
}
inline bool empty()
{
return !tp;
}
inline bool in(int x)
{
return ins[x];
}
void print(int x)
{
int fro=tp;
while(s[fro]!=x) {
--fro;
}
printf("%d\n",tp-fro+1);
for(int i=fro;i<=tp;++i) {
printf("%d ",s[i]);
}
putchar('\n');
return;
}
}s;
void init()
{
flag=0;
s.init();
return;
}
void search(int x)
{
if(s.in(x)) {
flag=1;
s.print(x);
return;
}
s.push(x);
search(to[x]);
s.pop();
return;
}
int main()
{
scanf("%d",&t);
while(t--) {
init();
scanf("%d",&n);
for(int i=1,a;i<=n;++i) {
scanf("%d",&a);
to[i]=i-a;
}
for(int i=1;i<=n;++i) {
search(i);
if(flag) {
break;
}
}
}
return 0;
}