Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)

A题 题目链接 (签到)

思路:根据题意,最终操作后,奇数不会变,偶数都会减1

#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=2e5+50;

int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        if(t&1) printf("%d ",t);
        else printf("%d ",t-1);
    }
 //   system("pause");
    return 0;
}

B题 题目链接 (签到)

题意:把一个数组分成k段,每段贡献为区间最大值,求总贡献最大值

思路:找出前k大的元素下标,然后下标按递增排序

PS:这题自己属实脑瘫= =、,在输出区间长度的时候,总是细节出问题,wa了三发

#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=2e5+50;
//题意:把一个数组分成k段,每段贡献为区间最大值,求总贡献最大值
//思路:找出前k大的元素下标
struct node
{
    int index,v;
    friend bool operator < (const node &f1,const node &f2)
    {
        if(f1.v!=f2.v) return f1.v>f2.v;//大的在前面
        else return f1.index>f2.index;
    }
};
vector<node>a;
int n,k;
vector<int> divd;
int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        a.push_back({i,t});
    }
    sort(a.begin(),a.end());
    int sum=0;
    for(int i=0;i<k;i++)
    {
        sum+=a[i].v;
        divd.push_back(a[i].index);
    }
    printf("%d\n",sum);
    sort(divd.begin(),divd.end());
    if(k==1)
    {
        printf("%d",n);
    }
    else if(k==2) printf("%d %d\n",divd[0],n-divd[0]);
    else
    {
        int X=divd.size();//下标的个数
        printf("%d ",divd[0]);
        int use=divd[0];//use记录输出了多少个  最终n-use就是最后一个区间的长度
        for(int i=0;i<X-2;i++)
        {
            printf("%d ",divd[i+1]-divd[i]);
            use+=divd[i+1]-divd[i];
        }
        printf("%d",n-use);
    } 
//    system("pause");
    return 0;
}

C题 题目链接 (双指针+坑)

题意:给定一个数列,把它分成3段 ,可以是空段。记每段的区间和为a b c, (a区间从首项开始 b为中间段 c末项结束) 问满足a==c 且a最大的分法下,a为多少。 (a c有可能取0)

思路:双指针,p1 p2刚开始分别指向首项和末项,代表左区间和右区间的最远扩展点。 然后不断向中间扩展 ,细节判断见代码

#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=2e5+50;
LL a[maxn];
int n;
int p1,p2;
int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",a+i);
    }
    p1=1,p2=n;//分别指首 末位置
    LL l=a[p1],r=a[p2];//l表示左段的和  r表示右段的和
    LL mmax=0;//记录最大答案
    while(p1<p2)
    {
        if(l==r)
        {
            mmax=max(mmax,l);//更新
            p2--;
            r+=a[p2];//l r都向中间扩展1格
            if(p1<p2) //l不能扩展到r的地盘
            {
                p1++;
                l+=a[p1];
            }
            continue;//注意,这里不能break  可能是1 2 2 3 3 2 2 1这种一直相等的情况
        }
        if(l>r)
        {
            p2--;
            r+=a[p2];
        }
        else 
        {
            p1++;
            l+=a[p1];
        }
    }
    cout<<mmax;
//    system("pause");
    return 0;
}

D题 题目链接 (字符串水题)

题意:给定两个字符串,我们能进行三种操作:第一种,将 a[i] 和 b[i] 的单个字符交;第二种,将 a[i] 和 a[n-i+1] 的单个字符交换;第三种,将 b[i] 和 b[n-i+1] 的单个字符交换。但是题目的要求是,让我们先做预处理替换a中的字符,再进行如上三种操作,能让字符串 a == b ,求预处理的次数。

思路:其实看懂了样例就不难,纯粹的模拟题。
我们每次需要比较a[i] a[n+1-i] b[i] b[n+1-i] 这4个元素 。看它们能不能两两配对相等 。出现诸如 a a b b / a b a b /这些情况 ,因为这些位置其实是互通的。

我们分两种情况讨论:
1、 b[i]==b[n-i+1]: 此时如果 a[i]!=a[n-i+1] ,操作次数就+1,否则+0
(这里我真傻,第一次写的时候不等的情况次数+2去了 = =、 又是因为太傻逼wa了)

2、 b[i]!=b[n-i+1] : 先看a[i] 会不会跟两个都不相等,如果会,次数+1 ;再看a[n-i+1] 会不会跟两个都不相等,如果会,次数+1。 这里有个特殊情况,如果a[i] a[n-i+1]都跟其中一个相等,可能是 a a /a b这种情况,操作次数得加1.

#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=1e5+50;
char s1[maxn],s2[maxn];
int len;
int ans;
bool cmp(int &a,int &b)
{
    return a>b;
}
int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d%s%s",&len,s1+1,s2+1);
    int p1=1,p2=len;
    while(p1<p2)
    {
        if(s2[p2]==s2[p1])
        {
            if(s1[p2]!=s1[p1])
            ans++;
        }
        else
        {
            int flag=0;
            if(s1[p1]!=s2[p1] && s1[p1]!=s2[p2]) flag++;//与两个都不相等
            if(s1[p2]!=s2[p1] && s1[p2]!=s2[p2]) flag++;
            if(flag==0 && s1[p1]==s1[p2]) flag++;//aa /ba 这种情况
            ans+=flag;
        }      
        p1++,p2--;
    }
    if(p1==p2 && s1[p1]!=s2[p1]) ans++;//奇数的时候判断中间
    cout<<ans;
  //  system("pause");
    return 0;
}

E题题目链接 (DFS)

这应该是写过最水的E题了…

题意:给定一棵n顶点的有根树,1为根。给出q个询问,每次询问以u为根的子树,dfs序第k个节点编号。 dfs过程优先遍历序号小的孩子节点。若不存在输出-1
(1<=n,q<=2e5)

思路:按照题目要求dfs一遍,order存dfs序,Hash[i]存第i个顶点在dfs序数组中的下标。num[i]存以i为根的树,有几个顶点。

#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=2e5+50;
struct 
{
    int to,next;
}edge[maxn];
int head[maxn],cnt=1;
int n,q;
int order[maxn];//dfs序
int cont=0;
int Hash[maxn];//第i个顶点在order中的下标
int num[maxn];//以i为根的树  搜到几个点
void add(int from,int to)
{
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int dfs(int rt)
{
    int ans=1;
    order[++cont]=rt;
    vector<int> a;
    for(int i=head[rt];i;i=edge[i].next)
    {
        int to=edge[i].to;
        a.push_back(to);
    }
    sort(a.begin(),a.end());//递增排序
    for(auto i:a)
    {
        ans+=dfs(i);
    }
    num[rt]=ans;
    return ans;
}
int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d%d",&n,&q);
    for(int i=2;i<=n;i++)
    {
        int fa;
        scanf("%d",&fa);//建有向边
        add(fa,i);
    }
    num[1]=dfs(1);
    for(int i=1;i<=cont;i++)
    {
        Hash[order[i]]=i;
    }
    while(q--)
    {
        int u,k;
        scanf("%d%d",&u,&k);
        if(k>num[u])
        {
            puts("-1");
        }
        else printf("%d\n",order[Hash[u]+k-1]);
    }
//    system("pause");
    return 0;
}

F题 题目链接 (双向dfs搜索+记忆化搜索)

题意:输入n,m,k,给出一个n*m的矩阵网格,每个格子有权值,从(1,1)一路异或到(n,m),只能向右或向下走,求到达(n,m)时,异或值等于k有多少种路径。
(1<=n,m<=20 0<=a[i][j],k<=1e18 )

异或性质:
1、a ^ b ^ a=b ^ a ^ a=b ( 一个数对另一个数异或2次 得原数)
2、a^ b^ c^ d^ e…进行异或运算,顺序不影响结果
3、若a ^ b=c 则 a ^ c=b 且 b ^ c=a
//扩展 abcd…x=y 则y替换左边任何一个 等式都成立

PS:这题一开始联想到AC自动机上状压dp,也想用类似的状压dp方法做。
但是这里的状态有1e18,太大了,所以考虑用map. 感觉思路没啥问题。
没想到提交后mle了 淦。后来用滚动数组优化了一维,居然还是mle。太草了! 可能是map耗内存太大了吧。

AC思路:dp不行,那只能记忆化搜索了。但是朴素的搜索估计会tle或mle 。大佬的题解里用了一个高级的玩意儿,双向搜索。以知了出发点和目的点的状态,分别从出发点开始dfs,搜到中间位置则停下 。再从目的点开始dfs,带着状态搜到中间点记录答案。

关于这里双向搜索,我说一下自己的理解: 从起点到终点,假设得到k的异或路径经过的数依次是x1,x2,x3,x4…xf 。那么只要在搜索过程中让这些数参与异或就一定可以得到k,我们假设在中间点经过的数为Xm,根据异或性质 ,x1,x2,x3…k…xf参与异或得到的一定是Xm ,所以双向搜索过程中,搜到中间的时候用k去参与异或得出这个Xm就可以了。

//#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=2e5+50;
map<LL,LL> dp[24*24];
LL g[25][25];
int n,m;
LL K;
LL ret=0;
int Hash(int i,int j)
{
    return i*23+j;
}
void dfs1(int x,int y,LL now)//now 现在的状态
{
    if(x+y==(n+m+2)/2)//到中间点
    {
        dp[Hash(x,y)][now]++;
        return;
    }
    if(x<n) dfs1(x+1,y,now^g[x+1][y]);
    if(y<m) dfs1(x,y+1,now^g[x][y+1]);
}
void dfs2(int x,int y,LL now)
{
    if(x+y==(n+m+2)/2)
    {
        ret+=dp[Hash(x,y)][now^K^g[x][y]];//中间点在dfs1时候异或了1次,现在的now中也异或了1次,所以再异或抵消1次
        return ;
    }
    if(x>1) dfs2(x-1,y,now^g[x-1][y]);
    if(y>1) dfs2(x,y-1,now^g[x][y-1]);
}
int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d%d%lld",&n,&m,&K);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%lld",&g[i][j]);
        }
    }
    dfs1(1,1,g[1][1]);
    dfs2(n,m,g[n][m]);
    cout<<ret;
    system("pause");
    return 0;
}

上mle的代码 ,思路我感觉是ok 的qaq

//#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
#include<unordered_map>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=0x3f3f3f;
const double eps=1e-5;
const int maxn=2e5+50;
unordered_map<LL,LL> dp[2][21];//滚动数组优化一维
LL g[25][25];
int n,m;
LL K;

int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    scanf("%d%d%lld",&n,&m,&K);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%lld",&g[i][j]);
        }
    }
    dp[1][1][g[1][1]]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(auto k:dp[i%2][j])//枚举出目前的全部状态,以得到下一个状态
            {
                    if(i+1<=n)
                    {
                        LL now1=k.first^g[i+1][j];
                        dp[(i+1)%2][j][now1]+=k.second;
                       
                    }
                    if(j+1<=m)
                    {
                        LL now2=k.first^g[i][j+1];
                        dp[i%2][j+1][now2]+=k.second;
                       
                    }              
            }
        }
        if(i!=n)
        {
            for(int j=1;j<=m;j++) dp[i%2][j].clear();
        }
    }
    cout<<dp[n%2][m][K];
    system("pause");
    return 0;
}
//mle on test 47
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值