[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 1≤i≤n1≤i≤n holds i−n≤ai≤i−1i−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 (1≤t≤1061≤t≤106). The description of the test cases follows.
The first line of each test case contains a single integer nn (1≤n≤1061≤n≤106).
The second line of each test case contains nn integers a1,a2,…,ana1,a2,…,an (i−n≤ai≤i−1i−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 (1≤s≤n1≤s≤n) — the number of elements in your subset.
In the second line, output ss integers i1,i2,…,isi1,i2,…,is (1≤ik≤n1≤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
2 5 0 1 2 3 4 4 -3 1 1 1
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 */