Codeforces Round #168 (Div. 1)

来源:互联网 发布:域名解析到端口 编辑:程序博客网 时间:2024/05/02 13:13

题目:http://www.codeforces.com/contest/274


A:如果出现两个数x,y使k*x=y,那么其任意一个都行;如果出现多个数成以k为倍数的等比,那么从最大或最小开始取,按顺序取一个舍弃一个,一定能是最优解。做法就是先排序,按大到小的顺序遍历,取a[i]的时候删去a[i]/k,这样取得的数的集合就是最大的集合

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<algorithm>#include<vector>#include<math.h>#include<queue>#include<set>#include<map>using namespace std;typedef long long ll;const int inf=1<<29;int a[100010];map<int,int> mp;int main() {int n,k;scanf("%d%d",&n,&k);for(int i=0;i<n;i++){scanf("%d",&a[i]);mp[a[i]]=i;}sort(a,a+n);int ans=0;for(int i=n-1;i>=0;i--){if(mp.find(a[i])==mp.end())continue;int t=a[i];if(t%k==0){mp.erase(t/k);}ans++;}printf("%d\n",ans);}


B:以1为根,从叶子节点往跟递推;假设u点的子节点(v1,v2,v3...)全为叶子节点,这些点变为0所要的操作是+k1,+k2,-k3,-k4....,将这些点按加减分成两类,需要加的点一起进行操作,要减的一起操作,这样最优,可以算出将这些点变为0的最少操作次数,这些操作导致u的值发生变化,再把u的值变0所需的操作次数也可以算出,那么就得到了最优情况下将以u为根的子树值都变成0所需的加的次数U[u]和减的次数D[u]。依次递推就能得到U[1],D[1]。答案就是U[1]+D[1]。

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<algorithm>#include<vector>#include<math.h>#include<queue>#include<set>#include<map>using namespace std;typedef long long ll;const int inf=1<<29;#define N 100010vector<int> e[N];ll ans,val[N];ll U[N],D[N];void dfs(int u,int pre){for(int i=0;i<(int)e[u].size();i++){int v=e[u][i];if(v==pre) continue;dfs(v,u);U[u]=max(U[u],U[v]);D[u]=max(D[u],D[v]);}val[u]+=U[u]-D[u];if(val[u]<0) U[u]-=val[u];else D[u]+=val[u];}int main() {int n,x,y;scanf("%d",&n);for(int i=1;i<n;i++){scanf("%d%d",&x,&y);e[x].push_back(y);e[y].push_back(x);}for(int i=1;i<=n;i++){scanf("%d",&x);val[i]=x;}dfs(1,1);printf("%I64d\n",U[1]+D[1]);}

D:把列之间的关系转化为图进行拓扑排序是很容易想到的一个突破口,关键在于如何建图。举一个只有一行的例子:1,2,3,4,5。点1比其他点都小,所以点1向其他4点各连一条边,点2往3,4,5连边,依次类推。。。这样正确但边数太多了。对于这个例子可以这样建图1->2->3->4->5 (每个点只向最小的比其大的点连边),边的数量大大减少。但如果是1,1,1,2,2,2这种数据又行不通了,所有的1都要向每个2连边,边的数量还是平方级别的。这里我们可以用添加虚拟节点的方法来解决,对于第2个例子,增加3个虚拟节点v1,v2,v3,v1向所有1连边,所有1向v2连边,v2向所有2连边,所有2向v3连边。这种方法保证边的数量是O(n*m)的

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<algorithm>#include<vector>#include<math.h>#include<queue>#include<set>#include<map>using namespace std;typedef long long ll;const int inf=1<<29;#define N 100010vector<int> e[N*3];int a[N],p[N];int cmp(int x,int y){return a[x]<a[y];}int cnt[N*3];vector<int> ans;int TopoOrder(int n) {int i, top = -1;for (i = 1; i <= n; ++i)if (cnt[i] == 0) {cnt[i] = top;top = i;}for (i = 1; i <= n; ++i)if (top == -1) {return 0;} else {int j = top;top = cnt[top];ans.push_back(j);for (int k = 0; k < (int)e[j].size(); ++k)if ((--cnt[e[j][k]]) == 0) {cnt[e[j][k]] = top;top = e[j][k];}}return 1;}int main() {int n,m,t;scanf("%d%d",&n,&m);t=m;for(int i=0;i<n;i++){for(int j=1;j<=m;j++){scanf("%d",&a[j]);p[j]=j;}sort(p+1,p+m+1,cmp);int j=1;while(j<=m&&a[p[j]]==-1) j++;for(t++;j<=m;t++){int k=j;while(k<=m&&a[p[k]]==a[p[j]]){e[t].push_back(p[k]);cnt[p[k]]++;e[p[k]].push_back(t+1);cnt[t+1]++;k++;}j=k;}}if(!TopoOrder(t)) printf("-1\n");else{for(int i=0;i<(int)ans.size();i++){if(ans[i]<=m) printf("%d ",ans[i]);}}}




原创粉丝点击