topcoder srm 691 div1 -3
1、给定一个$n$个顶点$n$个边的图,边是$(i,a_{i})$,顶点编号$[0,n-1]$。增加一个顶点$n$,现在选出一个顶点集$M$,对于任意的在$M$中 的顶点$x$,去掉边$(x,a_{x})$,增加边$(x,n)$。最后使得顶点0和1相连。有多少种$M$?
思路:设从0开始可以遍历的顶点集合为$A$,从1可以遍历的顶点集合为$B$,$C=A\bigcap B$。令$A^{'}=A-C,B^{'}=B-C$。那么有下面的情况:
(1)在$C$中选择一些点(至少一个),从$A^{'},B^{'}$中任意选点;
(2)在$C$中不选择点,从$A^{'},B^{'}$中任意选点(不能为空);
(3)假设$C$不为空,那么可以不从$A^{'},B^{'},C$中选择。
对于不在$A^{'},B^{'},C$中的点都是随便选。
#include <stdio.h> #include <string> #include <stack> #include <vector> #include <string.h> #include <algorithm> #include <assert.h> using namespace std; const int N=55; #define B(x) (1ll<<(x)) class Sunnygraphs { public: long long count(vector<int> a) { int n=(int)a.size(); vector<int> mask(n); for(int i=0;mask[i]<1;i=a[i]) mask[i]|=1; for(int i=1;mask[i]<2;i=a[i]) mask[i]|=2; int c[4]={0,0,0,0}; for(int i=0;i<n;++i) ++c[mask[i]]; long long ans=0; ans+=(B(c[3])-1)*B(c[1])*B(c[2]); ans+=(B(c[1])-1)*(B(c[2])-1); if(c[3]) ++ans; ans<<=c[0]; return ans; } };
2、给定$n$组数字$(a_{i},b_{i})$,$n$为偶数。现在重新排列这$n$组数字,得到新的$(A_{i},B_{i})$,使得下面的值最大:
$ans=\sum_{i=1}^{\frac{n}{2}}(B_{i}\sum_{j=1}^{i}A_{j})+\sum_{i=\frac{n}{2}+1}^{n}(B_{i}(X+\sum_{j=1}^{i}A_{j}))$
其中$2\leq n\leq 50,1\leq a_{i}\leq100000,1\leq b_{i}\leq10,0\leq X\leq 100000$
思路:现在考虑考虑最后分在前一半的两组$(A_{i},B_{i}),(A_{j},B_{j})$,若$i$在前优于$j$在前,那么$A_{i}B_{i}+(A_{i}+A_{j})B_{j}\geq A_{j}B_{j}+(A_{i}+A_{j})B_{i}$,即$A_{i}B_{j}\geq A_{j}B_{i}$。
由于$b_{i}$较小,现在枚举最后后一半的数字的所有的$A_{i}$之和$S$,那么现在对于一个数对,可以直接枚举它在前一半还是后一半(现在不管它在前一半还是后一半都可以直接计算对答案的贡献),这样可以进行动态规划。令$f[i][j][k]$表示现在已经考虑了$i$个数字,后一半数字的个数为$j$,后一半数字的$B$之和为$k$能得到的最大值,答案为$f[n][n/2][S]$。
#include <stdio.h> #include <string> #include <stack> #include <vector> #include <string.h> #include <algorithm> #include <assert.h> using namespace std; int cmp(pair<int,int> a,pair<int,int> b) { return a.first*b.second<b.first*a.second; } int f[55][33][505]; int A[55],B[55]; void up(int &x,int y) { if(x<y) x=y; } int n; void DP(const int NextSumB,const int X) { memset(f,-1,sizeof(f)); f[0][0][0]=0; int pre=0; for(int i=1;i<=n;++i) { pre+=B[i]; for(int j=0;j<=n/2;++j) for(int k=0;k<=pre;++k) { if(f[i-1][j][k]>=0) { up(f[i][j][k],f[i-1][j][k]+A[i]*(pre-k)+A[i]*NextSumB); up(f[i][j+1][k+B[i]],f[i-1][j][k]+A[i]*B[i]+A[i]*k+X*B[i]); } } } } class Moneymanager { public: int getbest(vector<int> a,vector <int> b,int X) { vector<pair<int,int> > p; n=(int)a.size(); int sum=0; for(int i=0;i<n;++i) { p.push_back(make_pair(a[i],b[i])); sum+=b[i]; } sort(p.begin(),p.end(),cmp); for(int i=1;i<=n;++i) { A[i]=p[i-1].first; B[i]=p[i-1].second; } int ans=0; for(int i=0;i<=sum;++i) { DP(i,X); ans=max(ans,f[n][n/2][i]); } return ans; } };