问题 A: 李时珍的皮肤衣
题目描述
例如今天(公元2018年6月17日)LSZ穿了3件皮肤衣服,会在公元2018年6月21日3件皮肤衣都会经历过透明变化。
输入
输出
样例输入
Input1:
3
Input2:
5
样例输出
Output1:
2
Output2:
2
提示
![](https://i-blog.csdnimg.cn/blog_migrate/e955de3ad4c4618b5f64843eedc227a9.png)
题解:找规律,公式:2^(n-1)+1
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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: 马大嘴的废话
题目描述
输入
接下来M行,每行一个字符串(只能由小写字母组成),代表当前MZH要说的一条废话信息。
输出
样例输入
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
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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的队列
题目描述
输入
输出
样例输入
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; 同一组的不能一起放,只有单独一个人的单独考虑;对于同一组的用阶乘就好了
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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); }