Codeforces Round #661 (Div. 3)D-E1

D. Binary String To Subsequences
题意:一个长度为n的字符串,可以从左到右拆分到多个集合中,保证该集合的前一个字符与后来的字符不同,问最少需要多少个集合。
题解:用两个数组t0和t1分别存储0和1模拟,每个0隶属于它出现时t1数组最后一个1的位置,每个1隶属于它出现时t0数组最后一个0的位置,若遇到10或01可以刚好可以凑成一队就都pop消去,若凑不成,ans++表示多开一个集合。

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define ll long long
#define endl "\n"
const int MAX=1e6+7;
int a[MAX];
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);
   int t;cin>>t;
   while(t--){
   	int n;cin>>n;
   	string s;cin>>s;
   	vector<int>t0,t1;
   	int ans=0;
   	for(int i=0;i<n;i++){
   		if(s[i]=='0'){
   			if(t1.size()==0){
   				ans++;
				a[i]=ans;
			}	
			else {
				int cnt=t1.back();
				a[i]=a[cnt];
				t1.pop_back();
			}
			t0.push_back(i);
		}
		else if(s[i]=='1') {
			if(t0.size()==0){
				ans++;
				a[i]=ans;
			}
			else{
				int cnt=t0.back();
				a[i]=a[cnt];
				t0.pop_back();
			}
			t1.push_back(i);
		}	
	}
	cout<<ans<<endl;
	for(int i=0;i<n;i++)cout<<a[i]<<" ";
	cout<<endl;    	
   }
   return 0;
} 

E1. Weights Division (easy version)
题意:有n个点的树,和n-1条边以及它的权值,可进操作:选择一条边 e d g e i edge_i edgei,使得改变的权值除2: w i / 2 w_i/2 wi/2,问最少需要多少次操作可以使得该树的根节点到各个叶子节点的权值和
∑ v ∈ l e a v e s w ( r o o t , v ) ≤ S ∑_{v∈leaves}w(root,v)≤S vleavesw(root,v)S

题意:dfs跑一遍求出各边出现的次数,优先队列从大到小存储进行操作后割边减少权值贡献,从大到小贪心进行除2操作并维护除2后的边。

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define ll long long
#define endl "\n"
const int mod=1e9+7;
const int MAX=1e6+7;
struct node
{
	ll w,num;
	bool operator <(const node a)const{
		return (w-w/2)*num<(a.w-a.w/2)*a.num;
	}
};
priority_queue<node>que;
vector<pair<ll,ll> >a[MAX];
ll n,s,ans,siz[MAX];
void dfs(int u,int fa)
{
	if(a[u].size()==1&&u!=1)siz[u]=1;
	for(auto t: a[u]){
		int v=t.first,w=t.second;
		if(v==fa)continue ;
		dfs(v,u);
		siz[u]+=siz[v];
		s-=siz[v]*w;
		que.push({w,siz[v]});
	}
}
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);
   int _;cin>>_;
   while(_--)
   {
   		cin>>n>>s;
   		for(int i=0;i<n-1;i++){
   			int u,v,w;cin>>u>>v>>w;
			a[u].push_back({v,w});
			a[v].push_back({u,w});	
		}
   		dfs(1,1);
   		ans=0;
   		while(s<0){
   			auto t=que.top();que.pop();		
			s+=(t.w*t.num-t.w/2*t.num);
			t.w/=2;
			que.push(t);
			ans++;
		}
   		cout<<ans<<endl;
   		for(int i=0;i<=n;i++){
   			a[i].clear();siz[i]=0;
		}
		while(!que.empty())que.pop();
	}
   return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值