题目链接
题意
给定一个长度为 n 的序列 a,保证 i−n≤ai≤i−1。
要求选出非空集合 S,使 ∑i∈Sai=0。
思路
由 i−n≤ai≤i−1,得 1≤i−ai≤n。
于是对于每个点 i,向 i−ai 连一条有向边。
图上的每个环都对应一个合法的点集 S。
为什么呢?
记 i 向 toi 连了边,由定义得 toi=i−ai。
一旦 S 形成了环,则 ∑i∈Si=∑i∈Stoi。
即 ∑i∈Si=∑i∈S(i−ai)。
将等式右边展开得:∑i∈Si=∑i∈Si−∑i∈Sai。
显而易见,∑i∈Sai=0,符合题目要求。
实现
本来应该挺好实现的。
自己脑抽 stack
写成 queue
WA 了 1 次。
数据组数 t≤1000000,我每次清空 vis 数组 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; }
|