[codeforces1270G]Subset with Zero Sum 数学 建图

题目:

Time limit2000 ms

Memory limit262144 kB

You are given nn integers a1,a2,,ana1,a2,…,an, such that for each 1in1≤i≤n holds inaii1i−n≤ai≤i−1.

Find some nonempty subset of these integers, whose sum is equal to 00. It can be shown that such a subset exists under given constraints. If there are several possible subsets with zero-sum, you can find any of them.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1t1061≤t≤106). The description of the test cases follows.

The first line of each test case contains a single integer nn (1n1061≤n≤106).

The second line of each test case contains nn integers a1,a2,,ana1,a2,…,an (inaii1i−n≤ai≤i−1).

It is guaranteed that the sum of nn over all test cases does not exceed 106106.

Output

For each test case, output two lines.

In the first line, output ss (1sn1≤s≤n) — the number of elements in your subset.

In the second line, output ss integers i1,i2,,isi1,i2,…,is (1ikn1≤ik≤n). All integers have to be pairwise different, and ai1+ai2++aisai1+ai2+⋯+ais has to be equal to 00. If there are several possible subsets with zero-sum, you can find any of them.

Example

Input
2
5
0 1 2 3 4
4
-3 1 1 1
Output
1
1 
4
1 4 3 2 

【题意】
一序列数a1,a2,…,an,满足 i−n≤ai≤i−1 (1≤i≤n)
求这个序列的一个子集,使子集的和为0

【思路】
i−n≤ai≤i−1
∴ 1<=i-ai<=n
构建一个图,对每个ai​,建一条从 i 到 i−ai 的边,由于每个点都有出度,整个图必定有环。
找到这个环,环上的ai之和为0.
example:
1-a1=2
2-a2=3
3-a3=1
把3个等式加起来得a1+a2+a3=0;
建边:1指向2,2指向3,3指向1

【坑】

不要忘记初始化
每个点都有一个出边,而且指向1~n内,所以找到一个环就可以停下了(首尾相接)
t的范围是(1~1e6)如果每次初始化都用memset,就是O(n)的,就T掉了,所以初始化要用for循环

找环直接判断首位相接,不要用tarjan
初始化不要用memset!

 1 #include<iostream>
 2 #include<vector>
 3 #include<stack>
 4 #include<cstring>
 5 using namespace std;
 6 int const maxn=1e6+7;
 7 int a[maxn],ans[maxn],n,to[maxn],sum,pos;
 8 bool vis[maxn];
 9 stack<int>sta;
10 inline int get_num(){
11     char ch=getchar();
12     bool flag=false;
13     int num=0;
14     while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
15     while(ch>='0'&&ch<='9'){num=(num<<1)+(num<<3)+ch-'0';ch=getchar();}
16     if(flag)return -1*num;
17     else return num;
18 }
19 void init(){
20     for(int i=0;i<=n;i++)vis[i]=0;
21     while(!sta.empty())sta.pop();
22     sum=0;pos=0;
23 }
24 int main(){
25     int t;
26     scanf("%d",&t);
27     while(t--){
28         n=get_num();
29         init();
30         for(int i=1;i<=n;i++){
31             a[i]=get_num();to[i]=i-a[i];
32             if(a[i]==0)pos=i;
33         }
34         if(pos){printf("1\n%d\n",pos);continue;}
35         else{
36             int p=1;
37             while(!vis[p]){
38                 sta.push(p);
39                 vis[p]=true;
40                 p=to[p];
41             }
42             while(sta.top()!=p){ans[++sum]=sta.top();sta.pop();}
43             printf("%d\n",sum+1);
44             for(int i=1;i<=sum;i++)printf("%d ",ans[i]);
45             printf("%d\n",p);
46         }
47     }
48     return 0;
49 }/*
50 题意:
51 一序列数a1,a2,…,an,满足  i−n≤ai≤i−1  (1≤i≤n)
52 求这个序列的一个子集,使子集的和为0
53 
54 思路
55  i−n≤ai≤i−1
56  ∴ 1<=i-ai<=n  
57  构建一个图,对每个ai​,建一条从 i 到 i−ai 的边,由于每个点都有出度,整个图必定有环。
58  找到这个环,环上的ai之和为0.
59  example:
60  1-a1=2
61  2-a2=3
62  3-a3=1
63  把3个等式加起来得a1+a2+a3=0;
64 建边:1指向2,2指向3,3指向1
65 
66 
67 这个题的坑:
68 不要忘记初始化
69 每个点都有一个出边,而且指向1~n内,所以找到一个环就可以停下了(首尾相接)
70 t的范围是(1~1e6)如果每次初始化都用memset,就是O(n)的,就T掉了,所以初始化要用for循环
71 
72 找环直接判断首位相接,不要用tarjan
73 初始化不要用memset!
74 */

猜你喜欢

转载自www.cnblogs.com/conver/p/12188110.html