Acdream原创群赛3(部分题解)
比赛网址链接:http://www.acdream.net/contest.php?cid=1010
A题: 按位异或。讨论情况。
C题:经典广搜。先打表存储各个数的因子。
G题:可以说是规律题。常规方法nlogn肯定超时。
H题:简单的二分匹配。
I题:树形dfs。
A
题目大意:给你两个有同样多元素的集合,求一个最小的x,x异或A中所有元素的结果都落在B集合中,且形成一一映射。
解题思路:关键就在于一一映射,并且先把所有的数都转换成二进制 => A,B中元素每位对应的0和1计算出来。假设第1位, A有a0个0,a1个1,b有b1个1,b0个0,
如果a0==b0&&a1==b1 那么x对应的此位必为0,否则a0==b1&&a1==b0,x对应的此位必为1.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 int a0[40], a1[40], b0[40], b1[40]; 8 int x[40]; 9 bool flag; 10 11 void cal() 12 { 13 for(int i=0; i<33; i++) 14 { 15 if(a0[i]==b0[i]&&a1[i]==b1[i]) x[i]=0; 16 else if(a1[i]==b0[i]&&a0[i]==b1[i]) x[i]=1; 17 else 18 { 19 flag=1; 20 break; 21 } 22 } 23 } 24 25 int main() 26 { 27 int n, t; 28 while(cin >> n) 29 { 30 for(int i=0; i<33; i++) 31 x[i]=a0[i]=a1[i]=b0[i]=b1[i]=0; 32 for(int i=0; i<n; i++) 33 { 34 scanf("%d",&t); 35 for(int j=0; j<33; j++) 36 { 37 if(t%2&1) a1[j]++; 38 else a0[j]++; 39 t/=2; 40 } 41 } 42 for(int i=0; i<n; i++) 43 { 44 scanf("%d",&t); 45 for(int j=0; j<33; j++) 46 { 47 if(t%2&1) b1[j]++; 48 else b0[j]++; 49 t/=2; 50 } 51 } 52 flag=0; 53 cal(); 54 if(flag) 55 { 56 cout << -1 <<endl; continue; 57 } 58 long long ans=0, tmp=1; 59 for(int i=0; i<33; i++) 60 { 61 ans+=x[i]*tmp; 62 tmp*=2; 63 } 64 printf("%lld\n",ans); 65 } 66 return 0; 67 }
C
题目大意:给出两个数a,b。 a可以变成a+d,的是a的因子。求a变成b最少需要多少步。
解题思路:先打表找出每个数的因子,注意1和本身也算。然后就是广搜了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 #include <vector> 5 #include <cmath> 6 #include <algorithm> 7 #include <cstring> 8 using namespace std; 9 10 const int maxn=100005; 11 int color[maxn]; 12 int a, b, Min, ok; 13 vector<int>vt[maxn]; 14 15 struct node 16 { 17 int t; 18 int step; 19 }; 20 21 void init() 22 { 23 for(int i=1; i<=maxn-5; i++) 24 { 25 int t=sqrt(i+1); 26 for(int j=1; j<=t; j++) 27 { 28 if(i%j==0) 29 { 30 vt[i].push_back(j); 31 if(i%(i/j)==0) vt[i].push_back(i/j); 32 } 33 } 34 } 35 } 36 37 void bfs() 38 { 39 node s,p; 40 memset(color,0,sizeof(color)); 41 queue<node>q; 42 s.t=a; 43 s.step=0; 44 q.push(s); 45 while(!q.empty()) 46 { 47 p=q.front(); 48 q.pop(); 49 if(p.t>b) continue; 50 if(p.t==b) 51 { 52 ok=1; 53 cout << p.step <<endl; 54 break; 55 } 56 for(int i=0; i<vt[p.t].size(); i++) 57 { 58 s.t=p.t+vt[p.t][i]; 59 s.step=p.step+1; 60 if(s.t<=b&&!color[s.t]) 61 { 62 color[s.t]=1; 63 q.push(s); 64 } 65 } 66 } 67 } 68 69 int main() 70 { 71 init(); 72 while(~scanf("%d%d",&a,&b)) 73 { 74 ok=0; 75 bfs(); 76 if(!ok) cout <<-1 <<endl; 77 } 78 return 0; 79 }
G:
题目大意:C[k]=∑A[i]⋅B[j]mod(109+7)。k=max(i,j)。
解题思路:开始我用nlogn的方法求c[k],TLE。 列几项仔细分析还是有规律可寻的。
c[k]=(a[1]+a[2]+……+a[k-1])*b[k]+b[1]+b[2]+……+b[k-1])*a[k]+a[k]*b[k]);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 3 #define mod 1000000007 4 #define maxn 100005 5 long long a[maxn], b[maxn]; 6 long long c[maxn]; 7 8 int main() 9 { 10 int n, t; 11 long long s1, s2; 12 while(scanf("%d",&n)!=EOF) 13 { 14 for(int i=0; i<n; i++) 15 c[i]=0; 16 for(int i=0; i<n; i++) 17 scanf("%lld",&a[i]); 18 for(int j=0; j<n; j++) 19 scanf("%lld",&b[j]); 20 s1=0, s2=0; 21 for(int i=0; i<n; i++) 22 { 23 c[i]=(c[i]+b[i]*s1+a[i]*s2+a[i]*b[i])%mod; 24 s1=(s1+a[i])%mod; 25 s2=(s2+b[i])%mod; 26 } 27 for(int i=0; i<n; i++) 28 { 29 if(i!=n-1) printf("%lld ",c[i]%mod); 30 else printf("%lld\n",c[i]%mod); 31 } 32 } 33 return 0; 34 }
H:
题目大意:给你一些边和一些点,求最大基数匹配。
解题思路:
看袁神的解题报告说什么Em什么什么的匹配,很复杂的说。 这题有那么复杂么,怎么感觉都是水水的二分匹配呀。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn=110; 8 int visit[maxn]; 9 int map[maxn][maxn]; 10 int match[maxn]; 11 int n; 12 13 bool find(int u) 14 { 15 for(int v=1; v<=n; v++) 16 { 17 if(map[u][v]&&!visit[v]) 18 { 19 visit[v]=1; 20 if(match[v]==-1||find(match[v])) 21 { 22 match[v]=u; 23 return true; 24 } 25 } 26 } 27 return false; 28 } 29 30 int hungary() 31 { 32 memset(match,-1,sizeof(match)); 33 int ans=0; 34 for(int i=1; i<=n; i++) 35 { 36 memset(visit,0,sizeof(visit)); 37 if(find(i)) ans++; 38 } 39 return ans; 40 } 41 42 int main() 43 { 44 while(cin >> n) 45 { 46 for(int i=1; i<=n; i++) 47 for(int j=1; j<=n; j++) 48 { 49 scanf("%d",&map[i][j]); 50 } 51 cout << hungary()/2 <<endl; 52 } 53 return 0; 54 }
I:
题目大意: 给你一颗树。让你删除最多的边,使得剩下的在一起的点还是偶数。
解题思路: 这题藐视困惑了我好久好久。从n以内的任意一个点开始都行,因为它是一颗树,然后进行树形dfs。把有子树是偶数个点的可以考虑删除。这里我们把要删除的边的下一个点做参照物,这样就能避免是删2个呢还是删4个或者删6个等等。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <vector> 5 #include <algorithm> 6 using namespace std; 7 8 const int maxn=100005; 9 int f[maxn]; 10 vector<int>vt[maxn]; 11 int n; 12 13 int dfs(int u, int fa) 14 { 15 f[u]=1; 16 for(int i=0; i<vt[u].size(); i++) 17 { 18 if(vt[u][i]==fa) continue; 19 f[u]+=dfs(vt[u][i],u); 20 } 21 return f[u]; 22 } 23 24 int main() 25 { 26 int u, v; 27 while(cin >> n) 28 { 29 for(int i=1; i<=n; i++) 30 vt[i].clear(); 31 for(int i=0; i<n-1; i++) 32 { 33 scanf("%d%d",&u,&v); 34 vt[u].push_back(v); 35 vt[v].push_back(u); 36 } 37 dfs(1,1); 38 int ans=0; 39 for(int i=1; i<=n; i++) 40 { 41 if(f[i]%2==0) ans++; 42 } 43 cout << ans-1 <<endl; 44 } 45 return 0; 46 }