沈阳集训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;
}
View Code

问题 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;
}
View Code

问题 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);
}
View Code

猜你喜欢

转载自www.cnblogs.com/EdSheeran/p/9277225.html
今日推荐