沈阳集训day5

问题 A: 李时珍的皮肤衣

题目描述

LSZ很皮!LSZ的皮肤衣更皮!

LSZ有很多件神奇的皮肤衣,而且LSZ总是喜欢一次穿上多件皮肤衣(一件套一件,而且一直穿好多天),这些皮肤衣有透明或不透明两种状态,当不透明的皮肤衣吸收了一天的阳光直射后,就会变成透明的皮肤衣,透明的皮肤衣能使阳光照射到里层皮肤衣,而透明的皮肤衣再吸收阳光,会在第二天会变成不透明的皮肤衣,不透明的皮肤衣会阻止阳光照射到里层皮肤衣。

LSZ从某天起(该天算作第1天)穿上N件皮肤衣(刚开始所有皮肤衣都是不透明的),问你最少要经过多少天,LSZ身上的皮肤衣都经历过透明变化?

例如今天(公元2018年6月17日)LSZ穿了3件皮肤衣服,会在公元2018年6月21日3件皮肤衣都会经历过透明变化。

输入

一行,只有一个整数N。

输出

最少的天数(对N取余数)

样例输入

Input1:
3
Input2:
5

样例输出

Output1:
2
Output2:
2

提示

数据范围:

20%的数据:N<=15

60%的数据:N<=10^7

100%的数据:N<=10^10

题解:找规律,公式:2^(n-1)+1

#include <bits/stdc++.h>

using namespace std;
#define ll long long
ll quik(ll a, ll b, ll mod){
    ll ret = 0;
    for(; b; b >>= 1, a = (a+a)%mod)
        if(b & 1)ret = (ret + a)%mod;
    return ret;
}
ll pow(ll a, ll b, ll mod){
    ll ret = 1;
    for(; b; b>>=1, a=quik(a, a, mod))
        if(b&1) ret = quik(ret,a, mod);
    return ret;
}
int main()
{
    ll n;
    scanf("%lld", &n);
    ll ans = pow(2, n-1, n);
    printf("%lld\n", ans+1);
    return 0;
}

问题 B: 马大嘴的废话

题目描述

MZH爱说废话,喜爱“水群”,经常被“提走”,管理员对MZH在群里说话进行了限制:

1、只能说小写英文字母。

2、长度不超过20。

即使这样,也不能阻止MZH“水群”,他在限制的条件下也说了成千上万条废话信息,现在已知MZH说过的N条废话信息,接下来MZH要说M条废话信息,请你回答:对于他说的每条废话信息在已知N条废话信息中出现的次数和。

比如:MZH说过了3条信息

1、 adbdb

2、 bcde

3、 cdbf

4、 db

现在说的信息为db,那么信息db在信息1和信息3和信息4出现过,所以答案为3.

输入

第一行,一个整数N。

接下来N行,每行一个字符串(只能由小写字母组成),代表MZH说过的N条废话信息。

再接下来一行,一个整数M。

接下来M行,每行一个字符串(只能由小写字母组成),代表当前MZH要说的一条废话信息。

输出

共M行,每行输出该废话信息出现的次数和。

样例输入

20
ad
ae
af
ag
ah
ai
aj
ak
al
ads
add
ade
adf
adg
adh
adi
adj
adk
adl
aes
5
b
a
d
ad
s 

样例输出

0
20
11
11
2

提示

数据范围:

20%的数据:n<=100 , m<=50

60%的数据:n<=10000, m<=500

100%的数据:n<=10000, m<=100000

题解:yy了一天的ac自动机,开始把文本串和匹配串搞反了,因为要找后面的匹配了多少次,所以后面建树,还是没有深刻理解,然后就是空间又开小了;

还有就是跳的时候要打时间戳,非常常用的一个方法。

ac自动机模板:https://www.cnblogs.com/EdSheeran/p/9216762.html

#include <bits/stdc++.h>
const int maxn = 100010;
using namespace std;
int siz, cur[maxn];
int flag[maxn*18];
struct ac{
    struct Sta{
        int son[26], fail;
        int cnt, rr;
    }sta[maxn*18];
    void init(){
        siz = 1;
        sta[0].fail = -1;
    }
    void insert(char *S, int tot){
        int len = strlen(S);
        int now = 0;

        for(int i = 0; i < len; i++){
            char c = S[i];
            if(sta[now].son[c-‘a‘] == 0)
                sta[now].son[c-‘a‘] = ++siz;
            now = sta[now].son[c-‘a‘];
        }
        sta[now].cnt++;
        cur[tot] = now;
    };

    void build(){
        queue <int> Q;

        Q.push(0);

        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            for(int i = 0; i < 26; i++){
                if(sta[u].son[i]){
                    if(u == 0)sta[sta[u].son[i]].fail = 0;
                    else {
                        int v = sta[u].fail;
                        while(v != -1){
                            if(sta[v].son[i]){
                                sta[sta[u].son[i]].fail = sta[v].son[i];
                                break;
                            }
                            v = sta[v].fail;
                        }
                        if(v == -1)sta[sta[u].son[i]].fail = 0;
                    }
                    Q.push(sta[u].son[i]);
                }

            }
        }
    }
    void get(int u, int ci){
        while(u != -1){
            if(sta[u].cnt && flag[u] < ci)
               sta[u].rr++;
            flag[u] = ci;
            u = sta[u].fail;
        }
    }
    void match(char *S, int ci){
        int now = 0, res = 0;
        int len = strlen(S);
        for(int i = 0; i < len; i++){
            char c = S[i];
            if(sta[now].son[c-‘a‘])now = sta[now].son[c-‘a‘];
            else {
                int p = sta[now].fail;
                while(p != -1 && sta[p].son[c-‘a‘] == 0)p = sta[p].fail;
                if(p == -1)now = 0;
                else now = sta[p].son[c-‘a‘];
            }
            //if(sta[now].cnt)
            get(now, ci);
        }

    }
    int Query(int t){
        return sta[cur[t]].rr;
    }

}Tr;
char a[maxn][55];
int main()
{
    //freopen("10.in","r",stdin);
    //freopen("10.out","w",stdout);
    int n ,m;
    scanf("%d", &n);
    Tr.init();
    for(int i = 1; i <= n; i++){
        scanf("%s", a[i]);
    }
    scanf("%d", &m);
    for(int i = 1; i <= m; i++){
        char s[30];
        scanf("%s", s);
        Tr.insert(s, i);
    }
    Tr.build();
    for(int i = 1; i <= n; i++){
       //memset(flag, 0, sizeof(flag));
        Tr.match(a[i], i);
    }
    for(int i = 1; i <= m; i++)
        printf("%d\n", Tr.Query(i));
    return 0;
}

问题 C: SSY的队列

题目描述

SSY是班集体育委员,总喜欢把班级同学排成各种奇怪的队形,现在班级里有N个身高互不相同的同学,请你求出这N个人的所有排列中任意两个相邻同学的身高差均不为给定整数M的倍数的排列总数。

输入

共三行:

第一行为N

第二行为N个不同的整数

第三行为M

输出

一行,为符合条件的排列总数(答案对1234567891取余数)。

样例输入

Input1:
5
-1 0 1 2 3
10
Input2:
4
1 2 3 4
3

样例输出

Output1:
120
Output2:
12

提示

数据范围:

20%的数据:N<=11

70%的数据:N<=15

100%的数据:N<=30,M<=1000。

题解:看数据范围,搜索吧;暴力回溯30分,那么怎么优化呢?

题上限制身高相差M的整数倍不能在一起,也就是满足height%M相同的不能站一起,相当于他们是同一组;

对于站队其实有很多是重复的,比如2个1和3个2排 <==> 2个10与3个8排,那么影响的就不是身高,而是几种类别各有多少个,状态就记忆话搜索(hash来存状态,map映射,涨姿势了);

那么我们就是记忆话搜索+回溯:a[i]表示分组,   c[i]表示人数是i的组有几个,  b[i]表示当前剩i个人的组又几个,用来hash; 同一组的不能一起放,只有单独一个人的单独考虑;对于同一组的用阶乘就好了

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

const int maxn = 100005;
#define ll long long
const ll bas=2333;
const ll mod=1234567891;
map<ll, ll>dp[33];
int a[33],cnt, b[33], n, p, c[33];
ll fac[33];
int mx=0;
bool vis[33];
ll dfs(int dep, int lst){
    if(dep > n)return 1;
    memset(b, 0, sizeof(b));
    for(int i = 0; i <= cnt; i++)
        if(i != lst) b[c[i]]++;
    ll st = c[0];
    for(int i = 1; i <= mx; i++)
        st = st*bas + b[i];
    st = st*bas + (lst?c[lst]:0);
    if(dp[dep].count(st)) return dp[dep][st];

    ll ret = 0;
    if(c[0] > 0){
        c[0]--;
        ret = (ret + dfs(dep+1, 0)) % mod;
        c[0]++;
    }
    for(int i = 1; i <= cnt; i++){
        if(i != lst && c[i] > 0){
            c[i]--;
            ret = (ret + dfs(dep+1, i)) % mod;
            c[i]++;
        }
    }

    return dp[dep][st] = ret;

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

    for(int i = 1; i <= n; i++){
        if(vis[i])continue;
        vis[i] = 1;
        int ret = 1;
        for(int j = i+1; j <= n; j++){
            if(a[i] == a[j]){
                vis[j] = 1; ret++;
            }
        }
        if(ret == 1)c[0]++;
        else c[++cnt] = ret;
        mx = max(mx, ret);
    }
    fac[0] = 1;
    ll ans = 1;
    for(int i = 1; i <= n; i++) fac[i] = fac[i-1] * i % mod;
    for(int i = 0; i <= cnt; i++) ans = ans * fac[c[i]] % mod;
    ans = ans * dfs(1, 0)%mod;
    printf("%lld\n", ans);
}

原文地址:https://www.cnblogs.com/EdSheeran/p/9277225.html

时间: 2024-10-29 01:40:20

沈阳集训day5的相关文章

暑假集训day5

今天的主要内容为最小生成树.判负环和差分约束系统 苗条的最小生成树 poj3522 本题排序完枚举最小边,Kruskal跑n遍即可. #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int INF=0x7fffffff; inline int read(){ int num=0,t=1;char c=getchar(); while(c>'9'||

雅礼集训 Day5

今日得分:100+0+40 = 140 题解 T1:考虑线性基,考虑每个数位是否会在线性基里,显然除了L的第一位1以外,其他位置只有从0变成1的时候才会加入线性基,对每一位分别判断即可 T2:考虑二分图匹配,对于每一个枚举到的一类点,我们把二类点按照极角序尝试匹配,显然按照这个顺序匹配不会出现冲突问题,因为如果一个点没选,那么直接形成匹配,如果一个点无法增广,那么它一定被选,不会对之后的点产生影响 T3:最后发现是求一个基环树森林的不同构标号个数,分别处理环和树,树哈希,环枚举循环节大小

FJ省队集训DAY5 T1

思路:考试的时候打了LCT,自以为能过,没想到只能过80.. 考完一想:lct的做法点数是100W,就算是nlogn也会T. 讲一下lct的做法把:首先如果一条边连接的两个点都在同一个联通块内,那么这条边对答案没有影响,可以忽略,因此,问题变成了每次询问两个点中路径上权值最大的边(这里的权值我们令它为加入这条边的时间),边我们用一个点连接两个端点来表示. 正解:由于是无根树,因此我们用并查集按秩合并,每次把小的加到大的里面去,询问的时候暴力走lct查找最大即可. 1 #include<cstdi

[蒟蒻修炼计划][谜之吐槽]常州集训day5

T1 Description 小W和小M一起玩拼图游戏啦~ 小M给小M一张N个点的图,有M条可选无向边,每条边有一个甜蜜值,小W要选K条边,使得任意两点间最多有一条路径,并且选择的K条边甜蜜值之和最大. Input 第一行三个正整数N,M,K. 接下来M行,每行三个正整数A,B,C表示A.B两点间有一条甜蜜值为C的无向边. Output 一行输出最大甜蜜值之和. Sample Input 5 4 3 1 2 10 1 3 9 2 3 7 4 5 3 Sample Output 22 HINT N

长沙集训day5(总结)

哇,转眼间已经来到这里5天了哇,真快.....哇.... 在这点消费就是高哇.....t t1:求一个数a的b次方,然后在,求a^b的因数和. 直接用快速幂求出a^b,边求边取膜(%%%%%%),然后最后从一循环到取膜玩的这个数,感觉程序没什么问题,但是 最后好像也就30分..... t2: 苏轼吃菜..... 苏轼吃个菜还贼讲究,输出-1........10分就到手了,也没有想到怎么写QAQ t3: 给你一个数列,你可以随机排列这个数列,那么方案数就是这个数的阶乘了哇,然后求出去abs(a[i

2015湖南省选集训DAY5——work(BZOJ4177)

Description Mike有一个农场,这个农场n个牲畜围栏,现在他想在每个牲畜围栏中养一只动物,每只动物可以是牛或羊,并且每个牲畜围栏中的饲养条件都不同,其中第i个牲畜围栏中的动物长大后,每只牛可以卖a[i]元,每只羊可以卖b[i]元,为了防止牛羊之间相互影响,Mike找到了m条规律,每条规律给出一个三元组(i, j, k)表示如果第i个围栏和第j个围栏养的是不同的动物,那么Mike就需要花费k的代价请人帮忙处理牛羊之间的影响.不过同时Mike也发现k条特殊的规则(S, a, b),表示如

福州3中集训day5

数论,zld神犇认为我们都学过数论的,讲了一波高端(入门?)操作,从扩展欧几里得开始,同余方程诸如此类,早晚得重修.连课件都没,拿着画图讲了一上午 sro_zld_orz 具体内容都记在本上. 还是说说下午考试题吧 T1,简单来讲就是定义一个函数f(n)=n的各个数位上的数字的和,使f(x)=f(n)-1; 求x的最大值,其实很简单,从贪心的角度讲,越接近原数,数字自然越大,而且各数位和还要是f(n)-1,那么我们不断/=n,知道n%10!=0时结束,然后我们把得到的这个数-1,再恢复原长即可.

沈阳集训day2

问题 A: 置换 题目描述 negii就是我们要将7(0111)翻转成14(1110),11(1011)翻转成13(1101). 现在我们给定二进制位数k以及n个满足二进制位数不超过k的数,我们需要输出对应翻转后的数的十进制表示 由于读入量较大,所以n个数由本机随机生成,具体生成方式如下 int my_rand() { Seed=(214013LL*Seed+2531011)&((1<<k)-1); return Seed; } 我们会给出初始的Seed,我们会调用n次函数,得到需要翻

2018暑假集训 DAY5 T4 幂运算

       本道题我没打出来,因为c++字符串处理不会用,后来托这个网站的福,打出来了,用了高精度类,300多行,应该是我打过最长的代码了吧. 1 #include<bits/stdc++.h> 2 using namespace std; 3 class Int{ 4 #define BASE 1000000000 5 public: 6 typedef long long value; 7 void New(size_t l){ 8 if (a!=NULL)delete[] a;a=ne