codeforces contest 349

http://codeforces.com/contest/349



//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

B 349B Color the Fence

题意:有v升颜料,涂1~9所花费的颜料是a[i],求涂成的数字最大

题解:大家都用贪心,只有我用背包,我好可怜啊。

解法一:完全背包

肯定是能涂数字越多越好,然后再越大越好。所以花费就是a[i],价值是1。

然后要标记路径。对于两种一样的方案,要维护数字比较大的方案。

解法二:贪心

先求出肯定能取cnt个。然后对于取的每一个,贪心的取最大的,如果取该个能保证总共输出的够cnt个,那么就可以取。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
const int V=1000005;
int a[15],d[V],ans[15],mrk[V];
void dfs(int v) {
	if(v<=0||mrk[v]==-1) return ;
	++ans[mrk[v]];
	dfs(v-a[mrk[v]]);
}
int main() {
	int v;
	while(~scanf("%d",&v)) {
		for(int i=1;i<=9;++i) scanf("%d",&a[i]);
		//比它小,花费还一样 
		for(int i=1;i<=9;++i) {
			for(int j=i+1;j<=9;++j) {
				if(a[j]==a[i]) {
					a[i]=v+1;
					break;
				}
			}
 		}
 		memset(d,0,sizeof(d));
 		memset(mrk,-1,sizeof(mrk));
 		for(int i=1;i<=9;++i) {
			for(int j=a[i];j<=v;++j) {
				if(d[j-a[i]]+1>d[j]) {
					d[j]=d[j-a[i]]+1;
					mrk[j]=i;
				} else if(d[j-a[i]]+1==d[j]&&i>mrk[j]) {
					mrk[j]=i;
				}
			}
		}
		if(d[v]==0) {
			puts("-1");
			continue;
		}
 		memset(ans,0,sizeof(ans));
 		dfs(v);
 		for(int i=9;i>=1;--i) {
 			for(int j=1;j<=ans[i];++j) {
 				printf("%d",i);
			}
		}
		puts("");
	}
    return 0;
}

#include<cstdio>
#include<algorithm>
using namespace std;
int a[15];
int main() {
	int v;
	while(~scanf("%d",&v)) {
		int zuixiao=100000;
		for(int i=1;i<=9;++i) {
			scanf("%d",&a[i]);
			zuixiao=min(zuixiao,a[i]);
		}
		int cnt=v/zuixiao;
		if(cnt==0) {
			puts("-1");
			continue;
		}
		for(int i=cnt-1;i>=0;--i) {//还要涂i个 
			for(int j=9;j>=1;--j) {
				if(v>=a[j]&&(v-a[j])/zuixiao>=i) {
					printf("%d",j);
					v-=a[j];
				}
			}
		}
		puts("");
	}
	return 0;
} 


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

C 348A Mafia

题意:n个人,第i个人最少要当a[i]次玩家。每场比赛有一个裁判,其他人都是玩家。求最少比赛次数。

题解:我好像是猜的啊??我真的好菜。

1,最少要比ans1=max(a[i])场。

2,接下来我们用集合来理解a数组。

一共有n个集合(n个人),第i个集合中最少有a[i]个元素(比赛)。

集合可以重合,但是因为不可能有n个人同时参加一场比赛,所以一个元素最多属于n-1个集合。

按照贪心的想法就是ans2=sum(a[i])/(n-1)

然后取max(ans1,ans2)就行了

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
ll a[100005];
int main() {
	int n;
	while(~scanf("%d",&n)) {
		ll ans1=0,ans2=0;
		for(int i=1;i<=n;++i) {
			scanf("%I64d",&a[i]);
			ans1=max(a[i],ans1);
			ans2=ans2+a[i];
		}
		if(ans2%(n-1)) {
			ans2=ans2/(n-1)+1;
		} else {
			ans2=ans2/(n-1);
		}
		printf("%I64d\n",max(ans1,ans2));
	}
    return 0;
}


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

D  348B  Apple Tree
题意:有一棵树,它的所有儿子的权重必须相等。权重的定义:叶节点有点权,其余节点的权重等于它的儿子的权重之和。
题解:好像就是一层一层推上去(太早之前做的了,有点忘)。54行去掉会wa,不知道为何。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
const int N=100005;
int a[N],last[N],sz;
struct Edge {
	int pre,to;
}e[2*N];
void inite() {
	sz=0;
	memset(last,-1,sizeof(last));
}
void adde(int u,int v) {
	e[++sz].to=v;
	e[sz].pre=last[u];
	last[u]=sz;
}
struct Node {
	ll napp;//这个分支的总苹果数
	ll d;//napp|d
}node[N];
ll gcd(ll a,ll b) {
    if(b==0) return a;
    return gcd(b,a%b);
}
ll lcm(ll a,ll b) {
    return a*b/gcd(a,b);
}
void dfs(int fa,int u) {
    ll mapp=-1;//儿子中最少的苹果
    ll dlcm=1;//儿子的d的lcm
    ll nson=0;//儿子个数
    for(int i=last[u];i!=-1;i=e[i].pre) {
        if(e[i].to==fa) continue;
        dfs(u,e[i].to);
        if(mapp==-1) {
            mapp=node[e[i].to].napp;
        } else {
            mapp=min(mapp,node[e[i].to].napp);
        }
        dlcm=lcm(dlcm,node[e[i].to].d);
        ++nson;
    }
    if(dlcm==0) dlcm=1;
    if(nson==0) {//叶子节点
        node[u].napp=a[u];
        node[u].d=1;
    } else {
        node[u].napp=mapp/dlcm*dlcm*nson;
        node[u].d=dlcm*nson;
    }
}
int main() {
	int n;
	while(~scanf("%d",&n)) {
        ll sum=0;//总共有几个苹果
		for(int i=1;i<=n;++i) {
			scanf("%d",&a[i]);
            sum=sum+a[i];
		}
		int u,v;inite();
		for(int i=1;i<n;++i) {
			scanf("%d%d",&u,&v);
			adde(u,v);
			adde(v,u);
		}

		memset(node,0,sizeof(node));
		dfs(1,1);
		printf("%I64d\n",sum-node[1].napp);
	}
    return 0;
}


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
E  348C  Subset Sums
题意:n个数,m个集合,q次操作。
"? k" :查询第k个集合的元素和
"+ k x" : 第k个集合每个元素加上x
题解:我好像还是第一次做分块的题目。
把集合分成大集合(大小大于SQRTN,个数肯定小于SQRTN个(因为集合大小之和是n))和小集合。
总思想就是每个大集合维护sum,add值,小集合暴力。这样每次查询的复杂度是sqrtN
大集合+:sum+=x
小集合+:暴力的每个去加,还要更新大集合的add
大集合?:sum,add
小集合?:暴力的每个去加,还要考虑大集合的add对他的影响
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100005;
const int SQRTN=317;
ll a[N],sum[SQRTN],add[SQRTN];
int sz[N],da[N],mrk[N],jiaoji[SQRTN][N],tmp[N];
vector<int> S[N];
int main() {
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;++i) scanf("%I64d",&a[i]);
    int szda=0;
    for(int i=1;i<=m;++i) {
        scanf("%d",&sz[i]);
        int x;
        for(int j=1;j<=sz[i];++j) {
            scanf("%d",&x);
            S[i].push_back(x);
        }
        if(sz[i]>=SQRTN) {
            da[++szda]=i;
            mrk[i]=1;
            for(int j=0;j<sz[i];++j) {
                int t=S[i][j];
                add[szda]=add[szda]+a[t];
            }
        }
    }
    for(int i=1;i<=szda;++i) {
        int ii=da[i];//第ii个集合是大集合
        memset(tmp,0,sizeof(tmp));
        for(int j=0;j<sz[ii];++j) tmp[S[ii][j]]=1;
        for(int j=1;j<=m;++j) {
            for(int k=0;k<sz[j];++k) {
                jiaoji[i][j]+=tmp[S[j][k]];
            }
        }
    }
    char op;int k,x;
    while(q--) {
        scanf(" %c %d",&op,&k);
        if(op=='+') {
            scanf("%d",&x);
            if(mrk[k]) {//大集合
                int kk;
                for(int i=1;i<=szda;++i) {
                    if(da[i]==k) {
                        kk=i;
                        break;
                    }
                }
                sum[kk]+=0ll+x;
            } else {
                for(int i=0;i<sz[k];++i) a[S[k][i]]+=0ll+x;
                for(int i=1;i<=szda;++i) {
                    add[i]+=1ll*x*jiaoji[i][k];
                }
            }
        } else {
            if(mrk[k]) {
                int kk;
                for(int i=1;i<=szda;++i) {
                    if(da[i]==k) {
                        kk=i;
                        break;
                    }
                }
                ll ans=sum[kk]*sz[k]+add[kk];
                for(int i=1;i<=szda;++i) {
                    if(i==kk) continue;
                    ans+=sum[i]*jiaoji[i][k];
                }
                printf("%I64d\n",ans);
            } else {
                ll ans=0;
                for(int i=0;i<sz[k];++i) ans+=a[S[k][i]];
                for(int i=1;i<=szda;++i) {
                    ans+=sum[i]*jiaoji[i][k];
                }
                printf("%I64d\n",ans);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值