Educational Codeforces Round 2

目录

A. Extract Numbers

B. Queries about less or equal elements

C. Make Palindrome

E. Lomsat gelral


A. Extract Numbers

题意:太多不想翻译

思路:字符串模拟,我们使用一个临时变量t去储存每个符号直接的字符串,同时需要注意的是,我们对于空字符串也进行处理,然后在输出的时候判断一下即可。

#include 
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair pll;


bool check(string s)//检查子段是否合法
{
	for(int i=0;i'9') return false;
	}
	return true;
}

void solve()
{
	string s;
	cin>>s;
	s+=";";
	vector c1,c2;
	string t;//储存每个符号之间的字符串
	for(int i=0;i> t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

B. Queries about less or equal elements

题意:给你两个整数数组 a和 b。对于第二个数组 bj中的每个元素,你应该找出数组 a中小于或等于数值 bj的元素个数

思路:大水题,Lower bound即可。

#include 
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair pll;




void solve()
{
	int n,m;
	cin>>n>>m;
	vector a(n),b(m);
	for(int i=0;i>a[i];
	for(int i=0;i>b[i];
	sort(a.begin(),a.end());
	for(int i=0;i=0){
			cout<> t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

C. Make Palindrome

题意:给定一个字符串,你可以将其中任意一个字符替换,代价为1,或是对其进行排列,代价为0,令其变为回文串的代价最小且输出字典序最小的回文串

思路:贪心,用一个桶去统计每个字符的出现次数,我们让每个字符的出现次数为偶数,且让这种情况的代价最小。对字符串按长度为奇偶进行讨论:

1,奇数:这个时候,有一个字符串的出现次数为奇数,因为所有字符的出现次数非奇即偶,所以我们对于所有出现次数为奇数的字符,对其次数减一,并将其放入一个vector中进行存储,最后我们一定可以得到一个长度为奇数的vector,此时的最优解为将其右半部分变为左半部分即可,例如:abcde,我们将其变为abcba,这样我们就只修改了d和e这两个字符,这样的花费一定最小。最后再将vector中的字符添加回桶中。

2,偶数:这个时候,只需要将为奇数的字符放入vector中然后进行变换即可。

#include 
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair pll;




void solve()
{
	string s;
	cin>>s;
	map mp;
	for(int i=0;i res;
		for(auto [x,y]: mp){
			if(y%2){
				mp[x]--;
				res.push_back(x);
			}
		}
		for(int i=0;i z(sz);
		z[sz/2]=res[res.size()/2];//长度为奇数的情况,最中间的字符直接赋值
		for(auto [x,y]: mp){//map的key本身有序,所以能直接构造
			if(y>0){
			y/=2;
			for(;y>0;y--){
			z[i]=z[sz-i-1]=x;
			i++;
			}
			}

		}
		for(auto x: z) cout< res;
		for(auto [x,y]: mp){
			if(y%2){
				mp[x]--;
				res.push_back(x);
			}
		}
		for(int i=0;i z(sz);
		for(auto [x,y]: mp){
			if(y>0){
			y/=2;
			for(;y>0;y--){
			z[i]=z[sz-i-1]=x;
			i++;
			}
			}
		}
		for(auto x: z) cout<> t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

E. Lomsat gelral

题意:给定一颗树,求每个子树中颜色出现次数最多的颜色编号之和。

思路:裸的树上启发式合并。

树上启发式合并:可以高效的用于统计子树信息(不带修改的那种),时间复杂度为nlogn,极为优秀。

基本操作:两遍dfs:

第一遍dfs处理所有节点的相关信息,dfs序,轻儿子和重儿子等。

第二边dfs:

1,先处理所有轻儿子节点,计算但不保留其对于答案的贡献

2,处理所有重儿子节点,保留其对答案的贡献

3,将轻儿子合并进重儿子,计算其对于答案的贡献

#include
using namespace std;
const int N=1e5+5;
typedef long long ll ;
const int maxv=4e6+5;
typedef pair pll;
 
int l[N],r[N],id[N],sz[N],hs[N],tot;
vector e[N];
int c[N],cnt[N];
ll maxcnt,sumcnt;
ll ans[N];
void dfs1(int u,int f)
{
    l[u]=++tot;
    id[tot]=u;
    sz[u]=1;
    hs[u]=-1;
    for(auto v: e[u]){
        if(v==f) continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(hs[u]==-1||sz[v]>sz[hs[u]]) hs[u]=v;
    }
    r[u]=tot;
}

void add(int x)
{
    x=c[x];
    cnt[x]++;
    if(cnt[x]>maxcnt) maxcnt=cnt[x],sumcnt=0;
    if(cnt[x]==maxcnt) sumcnt+=x;
}
void del(int x)
{
    x=c[x];
    cnt[x]--;
}

void dfs2(int u,int f,bool keep)
{
    for(auto v: e[u]) {
        if(v!=f&&v!=hs[u]) dfs2(v,u,false);
    }
    if(hs[u]!=-1) dfs2(hs[u],u,true);
    for(auto v: e[u]){
        if(v!=f&&v!=hs[u]){
            for(int x=l[v];x<=r[v];x++ ) add(id[x]);
        }
    }
    add(u);
    ans[u]=sumcnt;
    if(!keep){
        for(int x=l[u];x<=r[u];x++) del(id[x]);
        sumcnt=0,maxcnt=0;
    }
}


void solve() 
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>c[i];
    }
    for(int i=1;i<=n-1;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(1,0);
    dfs2(1,0,false);
    for(int i=1;i<=n;i++){
        cout<>t;
    while(t--){
        solve();
    }
    system("pause");
    return 0;
}

你可能感兴趣的:(cf补题,算法,数据结构,启发式算法)