Codeforces Round #498 (Div. 3) 题解

期末考完后,重新开始刷题生涯- -

传送门:http://codeforces.com/contest/1006

种种原因,卡B题卡了很久,出了c题以后实在困得不行,DEF三题都是后来补的。

目录

A题:Adjacent Replacements

B题:Polycarp's Practice

C题:Three Parts of the Array

D题:Two Strings Swaps

E题:Military Problem

F题:Xor-Paths



  • A题:Adjacent Replacements

    要将所有的奇数不变,将所有的偶数减一。。。。。。


  • B题:Polycarp's Practice

    总共有n个难题,要求正好在k天,每天至少做一个难题。每个难题都有一个难度系数,当天所收益的为当天所做的题中难度系数最大的一题,要求总的收益,并列出得到最大收益的时候如何分配日期。

     思路:这题写挂了挺久的。具体思路和出题者差不多,首先找到前k大的数,然后用这k个数进行延伸,求得具体的安排方案。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 20005;

int n,k;
vector<int> a,e,b;
long long ans;

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
    	int t;
        scanf("%d",&t);
        a.push_back(t);
        b.push_back(t);
	}
    e.push_back(0);
    sort(a.begin(),a.end(),greater<int>() ); 
    for(int i=0;i<k;i++)
    {
    	int t = a[i];
        ans += t;
        int l = find(b.begin(), b.end(), t)-b.begin()+1; //找到第一次出现的位置,这步可以用map预处理省略掉
        b[l-1] = -1;
        e.push_back(l);
    }
    cout<<ans<<endl;
    sort(e.begin(),e.end());
    for(int i=1;i<k;i++)
    {
    	cout<<e[i]-e[i-1]<<" "; //两个相减得到相隔的日期
    }
    cout<<n-e[k-1]<<endl;//由于最后一天比较特殊,所以要用总日期减去最后一个数
    return 0;
}

  • C题:Three Parts of the Array

    题意:给我们n个数,要求把它们分成3段,使得第一段和第三段它们各自子序列的和相等,求满足条件的最大的子序列的和。

    注意:每一段的长度可以为0!!!

    题解:这题做的比第二题快多了- -,   设两个flag i=0,j=n-1,一个从前往后走,另一个从后往前走,并用l和r分别维护它们的前(后)缀和,如果某一个比较大,那么这一个就停下来让另一个走。如果两个一样大,那么就一起继续走。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 200005;

int n,d;
long long k,l,ans;
long long a[maxn];

int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&a[i]);
	}
	int i=0,j = n-1;
	k = a[i];
	l = a[j];
	while(i<j)
	{
//		cout<<i<<" "<<k<<"----"<<j<<" "<<l<<endl;
		if(k==l) {
			ans = k;
			i++;
			j--;
			k+=a[i];
			l+=a[j];
		}
		else if(k<l) {
			i++;
			k += a[i];
		}
		else if(k>l) {
			j--;
			l += a[j];
		}
	}
	cout<<ans<<endl;
	return 0;
}

  • D题:Two Strings Swaps

    题意:给你两串长度为n的字母序列,要求仅允许完成3项操作:

  1.     交换a[i]和b[i];
  2.     交换a[i]和a[n-i+1];
  3.     交换b[i]和b[n-i+1];

    问最少需要替换几个a中的字母,并进行若干次上述操作,才能使a和b序列一模一样。

    题解:

            首先用a1,a2,b1,b2来分别代表a[i],a[n-i+1],b[i],b[n-i+1]。然后判断a1,a2,b1,b2中有几个相等的,如果4个都相等,那么a中无需替换。如果3个相等,那么就要替换掉一个。其他依次类推,需要注意的是:只有a中的字母能被替换!!!

#include <bits/stdc++.h>
using namespace std;

const int maxn = 200005;
string a,b;
int n;
long long ans;

long long cal(char a1,char a2,char b1,char b2)
{
    map<char,int> mp;
    mp[a1]++;
    mp[a2]++;
    mp[b1]++;
    mp[b2]++;
    vector<long long> e;
    sort(e.begin(),e.end());
    for(auto i:mp)
    {
        e.push_back(i.second);
    }
    if(e.size()==4) {
        return 2;
    }
    else if(e.size()==1) {
        return 0;
    }
    else if(e.size()==2) {
        if(e[0]==2)
            return 0;
        else
            return 1;
    }
    else if(e.size()==3)
    {
        if(a1==a2)
            return 2;
        return 1;
    }
}

int main()
{
    cin>>n;
    cin>>a>>b;
    int i=0,j=n-1;
    for(i=0,j=n-1;i<=j;i++,j--)
    {
        if(i==j) {
            if(a[i]!=b[j])
                ans++;
        }
        else
            ans += cal(a[i],a[n-i-1],b[i],b[n-i-1]);
        //cout<<a[i]<<" "<<b[i]<<" "<<ans<<endl;
    }
    cout<<ans<<endl;
    return 0;
}

  • E题:Military Problem

    题意:给一棵树,给你若干组询问i和j,求从第i个节点开始进行深度优先遍历,输出第j个被搜索到的编号,如果没有那么多子节点,则输出-1。

    题解:刚开始写了个裸的DFS,果断超时,因为面对n组询问,这样肯定过不了。

              寻求改进后,可以从第一个开始遍历,记录所有节点被遍历到的顺序。但是存在有的节点,它的子节点没有那么多,但是它有兄弟节点(深度优先遍历中遍历完一个节点后会继续搜索它的兄弟节点)。所以要预处理一下每个节点的子节点个数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>

using namespace std;

const int maxn = 200005;
long long ans,tot;
int n,q,f[maxn]; //a表示第i个的上级是谁
vector<int> e[maxn],a; //e表示第i个的下级有哪些
bool vis[maxn];

void solve(int cnt,int k)
{
    a.push_back(cnt);
    f[cnt] = 1;
    for(int i=0;i<e[cnt].size();i++)
    {
        if(!vis[e[cnt][i]])
        {
            vis[e[cnt][i]] = 1;
            solve(e[cnt][i],k);
            vis[e[cnt][i]] = 0;
            f[cnt] += f[e[cnt][i]];
        }
    }
}

int main()
{
    cin>>n>>q;
    for(int i=1;i<=n-1;i++)
    {
        int temp;
        scanf("%d",&temp);
        e[temp].push_back(i+1);
    }
    for(int i=1;i<=n-1;i++)
        sort(e[i].begin(),e[i].end());
    solve(1,n);
    int p[maxn];
    memset(p,-1,sizeof(p));
    for(int i=0;i<a.size();i++)
    {
        //cout<<a[i]<<" ";
        p[a[i]] = i;
    }
    //cout<<endl;
    //for(int i=1;i<=n;i++)
      //  cout<<f[i]<<" ";
    //cout<<endl;
    for(int i=0;i<q;i++)
    {
        int u,k;
        scanf("%d%d",&u,&k);
        if(p[u]+k<=n&&f[u]>=k)
            cout<<a[p[u]+k-1]<<endl;
        else
            cout<<-1<<endl;
    }
    return 0;
}

  • F题:Xor-Paths

    题意:从(1,1)点开始,只向右或者下走,要求走到(n,m)点。每个点有一个值a[i][j],要求所走路径的所有值xor起来等于k,问这样的路径有几条。

    题目思路:这是一条不错的题目。

                  首先想到的就是普通的dfs,但是会超时。所以学习使用双向DFS,从(0,0)点开始搜索,到x+y-1=max(m,n)为止,而这些终止的点在图上看是一条直线(y=-x+1+max(m,n) ),再用一个dfs从后往前(只能往左或往上)搜,同样是搜到x+y-1=max(m,n)为止,这样它们的终止节点也在这条线上,并且这可以看作是从头走到尾(自己理解一下)。中间有一些用map处理的技巧,需要自己了解。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 25;

long long n,m,k,a[maxn][maxn],ans=0,mn;
map<long long,long long> mp[25]; //刚开始map<int,long long>没有过,但是map<long long,int>过了
int dir[4][2] = {1,0,0,1,-1,0,0,-1};

void dfs1(long long x,long long y,long long cnt)//从前往后时,开始的xor值为0
{
    cnt ^= a[x][y]; //先异或再判断,保证到达中间那条线时是异或过那个结果的
    if(x+y+1==mn)
    {
        mp[x][cnt]++;  //由于是一条斜线,所以x值不会重复,用mp[x][now]来记录走到这个节点,异或值为cnt的路线条数
        return;
    }
    if(x+1<n) dfs1(x+1,y,cnt);
    if(y+1<m) dfs1(x,y+1,cnt);
}

void dfs2(long long x,long long y,long long cnt) //从k开始异或,异或到中间那条线,满足条件的异或值为之前求得的cnt的值,路线个数就是mp[x][cnt]
{
    if(x+y+1==mn)
    {
        ans += mp[x][cnt];
        return;
    }
    cnt ^= a[x][y]; //先判断再异或,保证中间那条线不会被运算两次
    if(x-1>=0) dfs2(x-1,y,cnt);
    if(y-1>=0) dfs2(x,y-1,cnt);
}

int main()
{
    cin>>n>>m>>k;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            scanf("%lld",&a[i][j]);
    mn = min(n,m);  //也可以是max(n,m),并不影响结果
    dfs1(0,0,0);
    dfs2(n-1,m-1,k);
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zxwsbg/article/details/81133677