ICPC Central Europe Regional Contest 2019部分题解

A:ABB

题目大意:给一个固定长度的字符串,问要在右边最少加几个字符能使它整体形成回文串。

思路:要在末尾加最短的字符形成回文串,其实也就是找最靠左的中心点,我们也知道中心点起码都在原来的串上,因为加一倍的长度是必定能构成回文串。所以我们只要找到能做中心点的且是最靠左的下标。那此时就运用
Manacher算法,算法中有一个数组p[i]是可以算出一个串中以i点为中心能构成的最长回文子串的半径(包含中心点)的,那么再回到本题中,显然,如果这个下标 i 加上它的(p[i]-1)还没到原字符串末尾的话,那么必然不能构成回文串,因为这种情况字符串自身的某些元素都无法构成回文,加元素也没用,也就是这个点无法作为中心点。那如果下标i加上(p[i]-1)刚好就等于字符串末尾,那么就好办了,只要把(i-(p[i]-1))前面的元素逆序加到末尾就可以了,也就是说这种情况是可以满足加元素形成回文串的。那其实我们也可以发现,对于任意一个下标,只有这两种情况,加上(p[i]-1)小于末尾下标,和加上(p[i]-1)等于末尾下标。那么这个时候问题就好解决了,直接从左边往后遍历,找到第一个加上(p[i]-1)等于末尾下标的元素,也就是从左往右数第一个能做中心点的下标,即最靠左的中心点。最终的答案还需根据Manache算法做一个判断然后计算。

#include<bits/stdc++.h>
using namespace std;
int Manacher(string str) {
	if (str.length() == 0) {
		return 0;
	}
	int len = (int)(str.length() * 2 + 1);
	char *chaArr = new char[len];
	int* pArr = new int[len];
	int index = 0;
	for (int i = 0; i < len;i++) {
		chaArr[i] = (i & 1) == 0 ? '#' : str[index++];
	}
	int R = -1;
	int C = -1;
	int maxn = 0;
	for (int i = 0; i < len; i++) {
		pArr[i] = R > i ? min(R - i, pArr[2 * C - i]) : 1;
		while (i + pArr[i]<len && i - pArr[i]>-1) {
			if (chaArr[i + pArr[i]] == chaArr[i - pArr[i]]) {
				pArr[i]++;
			}
			else {
				break;
			}
		}
		if (i + pArr[i] > R) {
			R = i + pArr[i];
			C = i;
		}
		maxn = max(maxn, pArr[i]);
	}
	for(int i=0;i<len;++i)
    {
        if(pArr[i]+i-1==len-1)
        {
            if(chaArr[i-pArr[i]+1]=='#')
                cout<<(i-pArr[i]+1)/2;
            else
                cout<<(i-pArr[i])/2;
            break;
        }
    }
	delete[] chaArr;
	delete[] pArr;
}
int main()
{
    int n;
    cin>>n;
    string x;
	cin>>x;
	Manacher(x);
}

C. Bob in Wonderland

题目大意:给n个点,n-1条边所构成的一个树(一定是连同无环图(树),因为n个点只有n-1条边,且题意中说明了所有节点在一个图中),然后通过拆边再重新接上的方式(拆开开一条边之后,其它的节点的其他边不受影响),算出最少多少步操作可以使得其变为一个直链。

思路:起初写这题的时候有点迷惑了,一眼望去跟图论一样。。。想着人家最小生成树是含环图变树,这题是树变直链,还想着有什么联系。结果看着这题贼多人写出来了,就想着可能真的是我想太多。结果确实,这题硬要说贪心也行,但我个人感觉有点像思维题,只不过这个思维比较简单。对于一个分岔路而言,一步操作最多最多每次也只能使得这个图减少一条岔路,而且是一定可以做到减少一条岔路。那这题就解决了,算下岔路个数就完事了。两种算岔路个数的方法:(1)1度节点个数-2(2)所有度大于2的节点的度数总和-2*度大于2的节点的总个数(就是每条岔路留下要走的那两条路之后还剩下多少条路)。显然第一个更简单算一些。

注意处理n=1这个特殊情况

#include<bits/stdc++.h>
using namespace std;
vector<int> G[3*100010];
int main()
{
    int n,s,e;
    cin>>n;
    if(n==1)
    {
        cout<<0;
        return 0;
    }
    for(int i=0;i<n-1;++i)
    {
        scanf("%d%d",&s,&e);
        G[s].push_back(e);
        G[e].push_back(s);
    }
    int con=0;
    for(int i=1;i<=n;++i)
    {
        if(G[i].size()==1)
            con++;
    }
    cout<<con-2;
}

E. Zeldain Garden

#include <bits/stdc++.h>
using namespace std;
#define PB push_back
#define ZERO (1e-10)
#define INF int(1e9+1)
#define CL(A,I) (memset(A,I,sizeof(A)))
#define DEB printf("DEB!\n");
#define D(X) cout<<"  "<<#X": "<<X<<endl;
#define EQ(A,B) (A+ZERO>B&&A-ZERO<B)
typedef long long ll;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
typedef pair<int,int> ii;
typedef vector<ii> vii;
#define IN(n) int n;scanf("%d",&n);
#define FOR(i, m, n) for (int i(m); i < n; i++)
#define F(n) FOR(i,0,n)
#define FF(n) FOR(j,0,n)
#define FT(m, n) FOR(k, m, n)
#define aa first
#define bb second
void ga(int N,int *A){F(N)scanf("%d",A+i);}
ll X,N,Q,S;
ll go(ll N){
    if(!N)return 0;
    Q=sqrt(N+ZERO),S=0;
    for(ll i=1;i<=Q;++i)S+=N/i;
    return ll(S*__int128(2)-__int128(Q)*Q);
}
int main(void){
    scanf("%lld%lld",&X,&N);
    printf("%lld\n",go(N)-go(X-1));
    return 0;
}

F. Light Emitting Hindenburg

题目大意:从n个数中取k个数出来,求k个数进行与运算后的最大值。

思路:典型贪心。算与运算后的最大值,那么就是尽量高位为1就行。那就直接从高位往低位遍历,对于第 i 位,去判断现存的数中有多少个第 i 位为1的,因为既然要使得与运算后第 i 位为1,而且只能取k个数字运算,那么也就是在现存的数字里面第 i 位是1的数字的数量要大于等于k,这样就能使得第 i 位是1,不然这一位只能是0。那么只要判断成功,我们就要把这第 i 位是1的数字赋值为现存数字,也就是答案就在这些数字里面,然后继续一位一位往下走,直到最后一位走完。那么最后让现存的数字(不一定是k个数,可能有多种组合情况)全部做与运算就是最终答案了。

#include<bits/stdc++.h>
using namespace std;
vector<int> a,b;
int main()
{
    int n,k,num;
    cin>>n>>k;
    for(int i=0;i<n;++i)
    {
        scanf("%d",&num);
        a.push_back(num);
    }
    for(int i=1<<29;i>0;i>>=1)
    {
        b.clear();
        for(int j=0;j<a.size();++j)
        {
            if(a[j]&i)
                b.push_back(a[j]);
        }
        if(b.size()>=k)
            a=b;
    }
    int ans=~0;
    for(int i=0;i<a.size();++i)
        ans&=a[i];
    cout<<ans;
}

或者(也没啥大区别,只是可以少写个for循环)

#include<bits/stdc++.h>
using namespace std;
vector<int> a,b;
int main()
{
    int n,k,num;
    cin>>n>>k;
    for(int i=0; i<n; ++i)
    {
        scanf("%d",&num);
        a.push_back(num);
    }
    int ans=0;
    for(int i=1<<29; i>0; i>>=1)
    {
        b.clear();
        for(int j=0; j<a.size(); ++j)
        {
            if(a[j]&i)
                b.push_back(a[j]);
        }
        if(b.size()>=k)
        {
            ans+=i;
            a=b;
        }
    }
    cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UIUC ICPC Spring Coding Contest 2019是UIUC(伊利诺伊大学厄巴纳-香槟分校)举办的一个编程比赛。UIUC ICPC Spring Coding Contest 2019是ACM国际大学生程序设计竞赛(ACM International Collegiate Programming Contest)的一部分。ACM国际大学生程序设计竞赛是世界上最具影响力的大学生计算机竞赛之一,每年吸引了来自全球各地的大学生参与。这个比赛旨在培养学生的算法和编程技能,提供一个展示和交流的平台。参赛者需要在规定时间内解决一系列编程问题。 参加UIUC ICPC Spring Coding Contest 2019对于那些对算法和编程有兴趣的学生来说,是一个很好的学习和锻炼机会。比赛中的问题通常涉及各种算法和数据结构,要求参赛者能够用编程语言实现有效和高效的解决方案。参赛者可以通过解决问题来提高他们的算法和编程技能,并与其他参赛者交流和学习。 在准备UIUC ICPC Spring Coding Contest 2019之前,建议参赛者先掌握一些基本的编程知识和技能,如数据结构、算法、编程语言等。参赛者可以参考一些相关的教程和学习资料,如GeeksforGeeks和HackerEarth等网站提供的编程教程。此外,还可以参考一些竞赛经验分享的文章和博客,了解其他人是如何准备和参加编程比赛的。 总之,参加UIUC ICPC Spring Coding Contest 2019是一个很好的机会,可以提高算法和编程技能,与其他参赛者交流和学习。准备比赛前,建议参赛者掌握基本的编程知识和技能,并参考一些相关的教程和学习资料。祝你在比赛中取得好成绩!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Awesome Competitive Programming Awesome](https://blog.csdn.net/qq_27009517/article/details/86593200)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值