【CH】【弱省胡策】Round #7 高精度+组合数+set+主席树+题答题+密码学

在CH上找了套题…
60滚粗啦!

T1的暴力懒得写,T3的提答好恶心…嘛就算都写上也就100分左右…我不会告诉你最后一个半小时左右我弃赛了…


Magic

背景

wys是TKD的妹子。

wys听说TKD总把题面写得很长很长;

于是这一次她要求TKD把题面写得很短很短。

描述

统计N个点N条边的简单无向连通图个数。

输入格式

一行一个整数N。

输出格式

输出一行一个整数,描述答案。

样例输入1

3

样例输出1

1

样例输入2

4

样例输出2

15

数据范围与约定

对于30%的数据:1<=N<=10;
对于60%的数据:1<=N<=50;
对于100%的数据:1<=N<=5000


暴力?

我想了这么几种暴力的写法:

1.先生成一棵树,然后再枚举第n条边怎么连,然后把环缩了,然后跑一遍prufer序列,然后判重…
2.枚举环的大小,然后再在环上的点上挂树

…算了我说不下去了…
总之…丧心病狂…

30分算法:
暴力+打表

60分算法:

用prufer序列知道有根树的大小是 nn1 ,然后枚举环的大小,组合数瞎搞…然而并不会瞎搞…

100分算法:

公式:

枚举环的大小k:

nnk1k(k1)!2Ckn

前两个是树的个数:现在只有n-k个节点可以选,但这n-k个节点要从n个节点中选,一共是 nnk1 棵有根树,还要乘k,表示挂在k个节点上。

后两个是环的个数:一个序列的全排列是 k! ,因为环是首尾相接的,重复了k次,所以是 (k1)! 个。环可以翻转,所以要除以2。从n个节点中选出k个节点,要乘以组合数。

然后这式子化简一下就是…

(n1)!+n!+n!(n12!+n23!+...+nn4(n3)!) (n>3)

好吧我弱我只能推出来n>3的,剩下的只能打表了

不推的话会因为高精T掉…别问我怎么知道的

高精除不能写高精除高精,会T,写高精除int,别问我怎么知道的

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

typedef long long LL;
const int SZ = 5010;
const int INF = 1000000010;

const int BASE = 1000000000;
const int WIDTH = 9;

struct bign{
    int num[3000],len;
    bign() { memset(num,0,sizeof(num)); len = 1;}
    bign (LL x)
    {
        memset(num,0,sizeof(num)); len = 0;
        do{
            num[++ len] = x % BASE;
            x /= BASE;
        }while(x);
    }
};

bool operator <(const bign &a,const bign &b)
{
    if(a.len != b.len) return a.len < b.len;
    for(int i = a.len;i >= 1;i --)
        if(a.num[i] != b.num[i])
            return a.num[i] < b.num[i];
    return false;
}

bign operator +(const bign &a,const bign &b)
{
    bign ans;
    int i = 1,x = 0;
    while(i <= a.len || i <= b.len)
    {
        x += a.num[i] + b.num[i];
        ans.num[i ++] = x % BASE;
        x /= BASE;
    }
    ans.num[i] = x;
    ans.len = i;
    while(ans.len > 1 && !ans.num[ans.len]) ans.len --;
    return ans;
}

bign operator -(const bign &a,const bign &b)
{
    bign ans;
    ans.len = a.len;
    int x = 0;
    for(int i = 1;i <= a.len;i ++)
    {
        x = x + a.num[i] - b.num[i] + BASE;
        ans.num[i] = x % BASE;
        x = x / BASE - 1;
    }
    while(ans.len > 1 && !ans.num[ans.len]) ans.len --;
    return ans;
}

bign operator *(const bign &a,const bign &b)
{
    bign ans;
    ans.len = a.len + b.len;
    for(int i = 1;i <= a.len;i ++)
    {
        LL x = 0;
        for(int j = 1;j <= b.len;j ++)
        {
            x = x + (LL)a.num[i] * b.num[j] + ans.num[i + j - 1];
            ans.num[i + j - 1] = x % BASE;
            x /= BASE;  
        }
        ans.num[i + b.len] = x;
    }
    while(ans.len > 1 && !ans.num[ans.len]) ans.len --;
    return ans;
}

bign operator /(const bign &a,const int &b)
{
    bign ans;
    ans.len = a.len;
    LL x = 0;
    for(int i = ans.len;i >= 1;i --)
    {
        x = x * BASE + a.num[i];
        ans.num[i] = x / b;
        x %= b;
    }
    while(ans.len > 1 && !ans.num[ans.len]) ans.len --;
    return ans;
}



void print(const bign &ans)
{
    printf("%d",ans.num[ans.len]);
    for(int i = ans.len - 1;i >= 1;i --)
        printf("%09d",ans.num[i]);
}

bign fac[SZ];

int main()
{
    int n;
    scanf("%d",&n);

    if(n <= 2) { puts("0"); return 0; }
    if(n == 3) { puts("1"); return 0; }

    bign tmp = 1,ans;

    for(int i = 1;i <= n - 1;i ++) 
        tmp = tmp * i; //(n - 1)!
    ans = tmp; 
    tmp = tmp * n; //n!
    ans = ans + tmp;//(n - 1)! * n!
    for(int i = 1;i <= n - 4;i ++)
    {
        tmp = tmp * n / (i + 1);
        ans = ans + tmp;
    }

    print(ans / 2);

    return 0;
}
/*
4999
*/

Rectangle

背景

wys是TKD的妹子。

wys听说TKD总把题面写得很长很长;

于是这一次她要求TKD把题面写得很短很短。

描述

给定一排n个柱子,第i个位置的高度为a_i

现在有m个操作,每次选择一个位置,将这个位置的高度减掉1

所有操作之前以及每次操作后输出当前的最大子矩形的大小

强制在线

输入格式

第一行两个正整数n,m,含义如题目中所示

接下来一行n个非负整数,第i个数a_i表示第i个位置的高度

接下来m行每行一个非负整数pos_i,代表将位置pos_i\oplus last_ans的柱子高度-1

last_ans表示上一次询问的答案,初始值为一开始的最大子矩形大小

输出格式

m+1行,每行一个正整数,代表第i-1次操作后的最大子矩形大小

样例输入

7 3
2 2 4 5 3 3 3
10
12
15

样例输出

15
14
10
8

数据范围与约定

对于30%的数据:1\leq n,m\leq 1000
对于另外20%的数据:1\leq n,m\leq10^5,a_i\leq 1
对于100%的数据:1\leq n,m\leq10^5,a_i\leq10^6

数据保证任何时刻所有位置的高度\geq 0

样例解释

四次询问的答案如下图所示:

https://img-blog.csdnimg.cn/2022010707255162050.png

来源

原创

提示

请注意输入和输出可能超过32位整型范围


//CH的图片地址好诡异…

三十分单调栈+二十分线段树,普及组都拿得到的分数

然后我不会做了,看不懂题解,弃疗

直接粘官方题解:

【100 分做法 1】
考虑将 50 分做法中的线段树改为可持久化线段树,每行维护一个
再用一个 set 维护一下所有行的最大连续区间*高度的最大值
由于每次修改只会修改一个位置,因此时空复杂度都是
O((n+m)log2h+h)
【100 分做法 2】
考虑将所有的连续最大子区间缩成一个 pair< int,int >扔进对应行的 set 里
这样就不需要可持久化线段树了
时间复杂度
O((n+m)log2h+h) ,空间复杂度 O(n+h)
【P.S.】
如果 h 很大怎么办?
由于有用的行数只有 n+m 行,离散化一下就好了 0.0

50分代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

typedef long long LL;
const int SZ = 1000010;
const int INF = 1000000010;
const double eps = 1e-6;

int n,m;
LL num[SZ];

struct haha{
    LL w,h;
}S[SZ];

int top = 0;

LL getans()
{
    LL ans = 0;
    for(int i = 1;i <= n;i ++)
    {
        LL d = 0;
        while(top && S[top].h > num[i]) 
            d += S[top].w,ans = max(ans,S[top].h * d),top --;
        S[++ top] = (haha){d + 1,num[i]};
    }
    LL d = 0;
    while(top) 
        d += S[top].w,ans = max(ans,S[top].h * d),top --;
    return ans;
}

struct segment{
    int l,r;
    LL lx,rx,sum,mx;
}tree[SZ << 2];

void update(int p)
{
    int lch = p << 1,rch = p << 1 | 1;
    tree[p].sum = tree[lch].sum + tree[rch].sum;
    tree[p].lx = max(tree[lch].lx,tree[lch].sum + tree[rch].lx);
    tree[p].rx = max(tree[rch].rx,tree[rch].sum + tree[lch].rx);
    tree[p].mx = max(max(tree[lch].mx,tree[rch].mx),tree[lch].rx + tree[rch].lx);
}

void build(int p,int l,int r)
{
    tree[p].l = l; tree[p].r = r;
    if(l == r)
    {
        tree[p].lx = tree[p].rx = max(0ll,num[l]);
        tree[p].mx = tree[p].sum = num[l];
        return ;
    }
    int mid = l + r >> 1;
    build(p << 1,l,mid);
    build(p << 1 | 1,mid + 1,r);
    update(p);
}

void change(int p,int pos,LL x)
{
    if(tree[p].l == tree[p].r)
    {
        tree[p].lx = tree[p].rx = max(0ll,x);
        tree[p].mx = tree[p].sum = x;
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(pos <= mid) change(p << 1,pos,x);
    else change(p << 1 | 1,pos,x);
    update(p);
}


int main()
{
//  freopen("T2.in","r",stdin); 
//  freopen("out.txt","w",stdout);      
    scanf("%d%d",&n,&m);
    bool flag = 0;
    for(int i = 1;i <= n;i ++)
    {
        scanf("%lld",&num[i]);
        if(num[i] > 1) 
            flag = 1;
    }
    if(!flag)
    {
        for(int i = 1;i <= n;i ++)
            if(num[i] == 0) num[i] = -INF;
        build(1,1,n);
        LL lastans = tree[1].mx;
        printf("%lld\n",lastans);
        while(m --)
        {
            LL pos;
            scanf("%d",&pos);
            pos ^= lastans;
            change(1,pos,-INF);
            lastans = tree[1].mx == -INF ? 0 : tree[1].mx;
            printf("%lld\n",lastans);
        }       
    }
    else
    {
        LL lastans = getans();
        printf("%lld\n",lastans);
        while(m --)
        {
            LL pos;
            scanf("%lld",&pos);
            pos ^= lastans;
            num[pos] --;
            lastans = getans();
            printf("%lld\n",lastans);
        }
    }
    return 0;
}
/*

*/



Secret

背景

wys是TKD的妹子。

wys听说TKD总把题面写得很长很长;

于是这一次她要求TKD把题面写得很短很短。

描述

这次的题面是有故事的>w<

有一天,TKD给wys发了一份密码,然后就默默地去码题了;

wys当然很不理解啊,发一串密码是个啥玩意儿;

然后呢,TKD很良心地给wys发了一份解密程序;

于是wys就能成功看懂了TKD发的东西了!

但是wys还是不满足,

她还想知道TKD是怎样把字符串加密的,

wys发现自己想不出来,所以想请你来帮帮她的忙。

帮wys写出加密程序的话,wys是会给你奖励的哦~

输入格式

第一行一个整数表示数据编号

接下来四行表示每一组数据的四个字符串。

输出格式

第一行一个整数表示数据编号

接下来四行表示每一组数据加密之后的密文。

TKD一共有五种加密方法,每种加密方法有10组数据。

wys已经收集到了全部的字符串,但是密文只收集到了一半。

即给选手的明文和输入文件完全相同(5行),密文只有输出文件中密文的一半(3行)。

提示

密文的格式是由若干个01串组成的;

01串的长度固定为8,两个01串之间用一个空格隔开;

每一个01串代表一个二进制数字;

也就是说这5种加密都是字符串→数字串的加密方式;

因为8位01串代表的是0~255之间的数,所以也可以理解为字符串→字符串的加密方式。

另外输入的字符串全部由字母数字空格以及常用字符&标点符号组成,不包括\0和\n。

对于给出的加密程序模板,

保证只会调用一次Srand();

保证不会调用无用的Rand();

不保证Len不变;

不保证交换两行输入字符串的顺序,得到的密文和原来一样;

即一个字符串对应的密文可能和上一个字符串有关;

并且仅可能和自己以及上一个字符串有关。

加密程序模板

#include<cstdio>
#include<cstdlib>
#include<cstring>

#include<iostream>
#include<algorithm>
using namespace std;

void _gets(char *UseS)
{
    gets(UseS);
    static int Len_of_U;
    Len_of_U=strlen(UseS);
    if(UseS[Len_of_U-1]=='\r')
        UseS[Len_of_U-1]=0;
}

namespace Random_Number
{
    unsigned Seed;

    void Srand(unsigned GetSeed)
    {
        Seed=GetSeed;
    }

    unsigned Rand(void)
    {
        Seed=Seed*1103515245+12345;
        return (Seed/65536)%32768;
    }
}

namespace Case0
{
    unsigned char GetC[_L];

    void Print(unsigned char Out)
    {
        static bool Save[8];
        for(int i=0;i<8;i++)
        {
            if(Out&1)
                Save[i]=true;
            else
                Save[i]=false;
            Out>>=1;
        }
        for(int i=7;i>=0;i--)
            if(Save[i])
                printf("1");
            else
                printf("0");
    }

    void Main()
    {
        static int Len;
        for(int Now=0;Now<4;Now++)
        {
            _gets((char*)GetC);
            Len=strlen((char*)GetC);
            ...
            for(int i=0;i<Len;i++)
                Print(GetC[i]),printf("%c",i^(Len-1)?' ':'\n');
        }
    }
}

namespace Case1
{
    unsigned char GetC[_L];

    void Print(unsigned char Out)
    {
        static bool Save[8];
        for(int i=0;i<8;i++)
        {
            if(Out&1)
                Save[i]=true;
            else
                Save[i]=false;
            Out>>=1;
        }
        for(int i=7;i>=0;i--)
            if(Save[i])
                printf("1");
            else
                printf("0");
    }

    void Main()
    {
        static int Len;
        for(int Now=0;Now<4;Now++)
        {
            _gets((char*)GetC);
            Len=strlen((char*)GetC);
            ...
            for(int i=0;i<Len;i++)
                Print(GetC[i]),printf("%c",i^(Len-1)?' ':'\n');
        }
    }
}

namespace Case2
{
    unsigned char GetC[_L];

    void Print(unsigned char Out)
    {
        static bool Save[8];
        for(int i=0;i<8;i++)
        {
            if(Out&1)
                Save[i]=true;
            else
                Save[i]=false;
            Out>>=1;
        }
        for(int i=7;i>=0;i--)
            if(Save[i])
                printf("1");
            else
                printf("0");
    }

    void Main()
    {
        static int Len;
        for(int Now=0;Now<4;Now++)
        {
            _gets((char*)GetC);
            Len=strlen((char*)GetC);
            ...
            for(int i=0;i<Len;i++)
                Print(GetC[i]),printf("%c",i^(Len-1)?' ':'\n');
        }
    }
}

namespace Case3
{
    unsigned char GetC[_L];

    void Print(unsigned char Out)
    {
        static bool Save[8];
        for(int i=0;i<8;i++)
        {
            if(Out&1)
                Save[i]=true;
            else
                Save[i]=false;
            Out>>=1;
        }
        for(int i=7;i>=0;i--)
            if(Save[i])
                printf("1");
            else
                printf("0");
    }

    void Main()
    {
        static int Len;
        for(int Now=0;Now<4;Now++)
        {
            _gets((char*)GetC);
            Len=strlen((char*)GetC);
            ...
            for(int i=0;i<Len;i++)
                Print(GetC[i]),printf("%c",i^(Len-1)?' ':'\n');
        }
    }
}

namespace Case4
{
    unsigned char GetC[_L];

    void Print(unsigned char Out)
    {
        static bool Save[8];
        for(int i=0;i<8;i++)
        {
            if(Out&1)
                Save[i]=true;
            else
                Save[i]=false;
            Out>>=1;
        }
        for(int i=7;i>=0;i--)
            if(Save[i])
                printf("1");
            else
                printf("0");
    }

    void Main()
    {
        static int Len;
        for(int Now=0;Now<4;Now++)
        {
            _gets((char*)GetC);
            Len=strlen((char*)GetC);
            ...
            for(int i=0;i<Len;i++)
                Print(GetC[i]),printf("%c",i^(Len-1)?' ':'\n');
        }
    }
}

int main()
{
    int Case;
    Random_Number::Srand(1);
    scanf("%d",&Case);
    static char Empty[10];
    gets(Empty);
    printf("%d\n",Case);
    Case/=10;
    if(Case==0)
        Case0::Main();
    if(Case==1)
        Case1::Main();
    if(Case==2)
        Case2::Main();
    if(Case==3)
        Case3::Main();
    if(Case==4)
        Case4::Main();
    return 0;
}

样例下载密码: ezv1

数据下载密码: c5y8


很容易看出来二十分是字符和数字一一对应的…只要抠出来就行了…好吧我也就到这个水平了233

直接粘官方题解:

首先我要自豪地说一句:
‘这是一道 wys 都只能拿 60 分的题! ’

并没有部分分算法
我们来分析一下加密算法吧。 。 。

首先拿到第一种加密方法,你可以发现:
‘这 NM 不就是一 SB 映射吗?’
然后要么打表,要么找到我是怎么随机全排列的,你就可以 A 掉了。

再看第二种加密,你可以稍微试试;
第一个数加密之后增加了一个定值,第二个数也同样。 。 。
这不就是把每个数都加了一个 Rand 吗?
一对比可以发现,的确加密就是直接顺着加 Rand。

第三种加密:
233 对应的密文有一个 0,2333 对应的密文有两个 0。
貌似只要做差就可以了。 。 。
然而第一个字母怎么做差?
观察一下,每次只用加一个 Rand 就好咯~

第四种加密:
啊这货有点难呢。 。 。 。然后你多试了几遍。 。 。
发现。 。 。咦?貌似对于每一位,结果都是一一对应的啊。 。 。
然后渐渐又能发现。 。 。对于每一位,明文每加 1,密文就会加一个定值。
然而那个定值只会是 3 or 53 or 101。
所以大胆猜测,加密方法为 X=X*P[Rand()%3]+Rand();
P 为{3,53,101}。
然后你就发现你猜对了。

第五种加密:
咦?密文都是一样长的?
那么长度一定的序列会是什么呢?
prufer 序列!
对了就是这货~
我们首先把输入的串的长度增长。 。 。增长到 254
然后再把这个序列看成一个 256 个节点的树的 prufer 序列
我们把它还原
假设根是 0,输出除了 0 之外所有节点的父亲。
我们就可以得到密文了~


这尼玛能做?…


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值