Codeforces Round #564 (Div. 2) Problem A->D

题目链接:http://codeforces.com/contest/1173


Problem A

按照题意暴力做即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#define debug(x) printf("----Line%s----\n",#x)
#define ll long long

using namespace std;

const int N = 1e5+5;

int x,y,z;

int main()
{
    while (~scanf("%d %d %d",&x,&y,&z)){
        if (x>z+y) printf("+\n");
        else if (y>x+z) printf("-\n");
        else if (x==y && z==0) printf("0\n");
        else printf("?\n");
    }
    return 0;
}

Problem B

棋子按顺序下右下右下右,

或者右下右下右下。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#define debug(x) printf("----Line%s----\n",#x)
#define ll long long

using namespace std;

const int N = 1e5+5;

int n;

int main()
{
    while (~scanf("%d",&n)){
        printf("%d\n",n/2+1);
        int nowx=1,nowy=1;
        for (int i=1;i<=n;i++){
            if (i%2==1) printf("%d %d\n",nowx++,nowy);
            else printf("%d %d\n",nowx,nowy++);
        }
    }
    return 0;
}

Problem C

题目中的操作是先放一张在底部,再取最顶上的

(一)首先最蠢的办法是把所有有数字的牌全部拿到手里,再一张张叠上去,所以最多也只需2n次操作。

(二)实际上还可以优化,对于在堆中的每张有数字的牌,假设上面数字为i,它在堆中位置为pi,那么在获得它之前的时间最明智的就是先把1,2,3...,i-1(假设都在手中)放上去“迎接”i的到来,当然放前i-1张之前的空白期就放空牌。

Example:

比如数字为4,在第七个位置,那么要取走它你要先逐渐往堆的底下塞七张牌,前四张塞空白牌,后三张塞1,2,3

 

也就是要正确放置,需要pi-(i-1)的缓冲时间(放空白)。

由此引出这个式子:pi -(i-1)+ n

当我们取得这个式子的最大值时,就是答案。

证明:

①证明拿走i之前,前i-1张已经在手里。

pi-i+1+n > pj-j+1+n

即pi-i > pj-j

若j<i,则pj必定小于pi

②证明大于i的牌一定可以及时到手中,每张牌x及时到手中的条件是缓冲时间(放空白牌)大于px-x+1,我们都取了最大值,所以显然成立。

由此,证毕。

其实(一)所说就是缓冲时间为n的最坏情况

(三)特殊判断堆中初始是xxxxx1234类似这样的状况(也就是一段后缀已经符合要求)

代码中注释着Part1,Part2的部分

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 2e5+5;

int a[N],b[N],p[N];

int main()
{
    int n;
    int pos1=0;
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",a+i);
    for (int i=1;i<=n;i++){
         scanf("%d",b+i);
         p[b[i]]=i;
         if (b[i]==1) pos1 = i;
    }

    int ans = n;

    ///特判Part 1
    bool flag = true;
    for (int i=pos1+1;i<=n;i++)
        if (b[i]!=i-pos1+1){
            flag = false;
            break;
        }
    ///特判Part 2
    int now_right = n-pos1+1;
    if (flag){
        for (int i=now_right+1;i<=n;i++)
            if (p[i]>=i-now_right){
                flag = false;
                break;
            }
    }
    ///输出答案
    if (flag){
        printf("%d\n",ans-now_right);
    }
    else {
        for (int i=1;i<=n;i++)
            ans = max(ans,p[i]-i+1+n);
        printf("%d\n",ans);
    }

    return 0;
}

总结:

①思维题的思考,也是建立在先有暴力再在这个基础上优化。所以先想最基础的暴力,别考虑各种杂七杂八的坑数据。


Problem D

先固定一号节点。

然后全排列所有直接相连的子节点。(也就是子节点个数的阶乘)

每个子节点代表其对应的一棵子树,当这棵子树在这个圈的连续一段上时,就不会有交叉。(自己画画把,可以先把直接相领的子节点画出来,接下来试试将子树的子节点伸到其他子树的段里,发现一定会交叉)

这个图可以加深对我说的全排列的理解

对于每棵子树分配的空间,对子树根节点和直接相连子节点全排列...就这样递归下去。(也就是(deg[i]+1)的阶乘)

然后最后再乘以n,因为1节点有n个位置可以选择。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#define debug(x) printf("----Line%s----\n",#x)
#define ll long long
#define mod 998244353

using namespace std;

const int N = 2e5+5;

struct node///链式前向星
{
    int to,last;
}edge[N<<1];
int head[N],id;

void add(int u,int v){edge[id].to = v; edge[id].last = head[u]; head[u] = id++;}

int son[N];
ll ans,jc[N];

void getjc()///预处理阶乘
{
    jc[0] = 1;
    for (ll i=1;i<=200000;i++){
        jc[i] = jc[i-1]*i%mod;
    }
}

void getson(int now,int pre){///获取子节点个数,pre表示父节点
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        if (v!=pre){
            son[now]++;
            getson(v,now);
        }
    }
}

void dfs(int now,int pre)///求答案
{
    if (now==1) ans = (ans*jc[son[1]])%mod;
    else ans = (ans*jc[son[now]+1])%mod;
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        if (v!=pre){
            dfs(v,now);
        }
    }
}

int main()
{
    int n,u,v;
    getjc();
    ans = 1;
    id = 1;
    scanf("%d",&n);
    for (int i=0;i<n-1;i++){
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }
    getson(1,1);
    dfs(1,1);
    printf("%I64d\n",ans*n%mod);
    return 0;
}

大大大总结:

①不会做记得跳,成功吸取教训,这次加了200多分应该可以慢慢掉了。

②思维题想不出当前最优解,先想最不优的最优解。(欲优化先暴力)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值