1.AC自动机:
UVALive 3942
题目描述:
给一个字符串集合,从这个字符串集合中选出一部分字符串凑另一个长字符串。每个字符串可以选多次。有多少种方案。
比如:
s = abcd
4个单词
abc
cd
ab
abcd
就有两种方案 abcd or ab+cd
代码:
#include <bits/stdc++.h>
#define _xx ios_base::sync_with_stdio(0);cin.tie(0);
using namespace std;
const int MAXN = 4000*105;
typedef long long ll;
int tot;
ll dp[MAXN];
struct Node {
int child[26];
int flag, fail;
int len; //当前结点字符串的长度
bool danger;
void init() {
memset(child, -1, sizeof child);
flag = 0;
fail = -1;
len = 0;
danger = false; //当前结点是否危险
}
}node[MAXN];
/*
* 向tire树中加入一个字符串
*/
void add(string& s) {
int p = 0;
for(int i = 0; i < s.size(); i++) {
int index = s[i] - 'a';
int l = node[p].len;
if(node[p].child[index] == -1) {
node[p].child[index] = ++tot;
p = tot;
node[p].init();
node[p].len = l + 1;
}
else p = node[p].child[index];
}
node[p].flag++;
node[p].danger = true;
}
/*
* 计算fail指针,将tire树变为tire图
*/
void getfail() {
queue<int> que;
que.push(0);
while(!que.empty()) {
int now = que.front();
que.pop();
for(int i = 0; i < 26; i++) {
int& to = node[now].child[i];
int& res = node[to].fail;
if(to != -1) {
int p = node[now].fail;
while(p != -1 && node[p].child[i] == -1) p = node[p].fail;
if(p == -1) res = 0;
else res = node[p].child[i];
que.push(to);
node[to].danger = node[to].danger || node[res].danger;
}
else {
if(now == 0) {
to = 0;
}
else to = node[node[now].fail].child[i];
}
}
}
}
/*
* 跑自动机+dp得到答案
*/
void getans(string& s) {
int p = 0;
for(int i = 0; i < s.size(); i++) {
int index = s[i] - 'a';
p = node[p].child[index];
int temp = p;
while(temp != -1 && node[temp].danger) {
int l = node[temp].len;
dp[i + 1] += dp[i + 1 - l]*node[temp].flag;
dp[i + 1] %= 20071027;
temp = node[temp].fail;
}
}
}
/*
* 初始化AC自动机
*/
void initAc_Auto() {
tot = 0;
node[0].init();
}
int main() {
_xx
string s, s1;
int T = 0;
while(cin >> s) {
int n;
cin >> n;
initAc_Auto();
for(int i = 0; i < n; i++) {
cin >> s1;
add(s1);
}
getfail();
memset(dp, 0, sizeof dp);
dp[0] = 1;
getans(s);
cout << "Case " << ++T << ": " << dp[s.size()] << endl;
}
return 0;
}
注:此题就题目而言可以直接用tire树dp做(不用ac自动机)62ms,也可以hash暴力dp做1800ms,看了别人的代码,真心觉得这种题有一万种ac方法,而我一种都不会!!!
2.扩展中国剩余定理
ll exgcd(ll a, ll b, ll& x, ll& y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
ll gcd = exgcd(b, a%b, x, y);
ll res = x;
x = y;
y = res - a/b*y;
return gcd;
}
ll lcm(ll a, ll b, ll gcd) {
return a/gcd*b;
}
/*
* 计算 x = a[i] mod m[i], 一共len个方程,下标从0开始
*/
ll exchina(ll a[], ll m[], ll len) {
ll x0 = a[0], mod = m[0];
for(int i = 1; i < len; i++) {
ll k1, k0, r = a[i] - x0;
ll gcd = exgcd(m[i], mod, k1, k0);
if((a[i] - x0)%gcd != 0) return -1;
k0 = r/gcd*k0;
ll res = m[i]/gcd;
k0 = (k0%res + res)%res;
x0 = x0 + k0*mod;
mod = lcm(mod, m[i], gcd);
x0 %= mod;
}
return x0;
}
3.LCA(dfs序 + RMQ)
#include <bits/stdc++.h>
#define _xx ios_base::sync_with_stdio(0);cin.tie(0);
#define mem(a, b) memset(a, b, sizeof a);
using namespace std;
typedef long long ll;
const int INFS = 0x3fffffff;
const ll INFB = 0x3fffffffffffffff;
const int MAXN = 100005;
vector<vector<int> > v;
int label[MAXN]; //队每个节点重新编号
vector<int> df; //dfs序
int fir[MAXN];
int dp[3*MAXN][32];
int t = 0; //初始化时间戳
int nsize[MAXN];
int dfs(int id, int w) {
label[id] = t++;
int nowid = label[id];
df.push_back(nowid);
fir[nowid] = df.size() - 1;
fz[nowid] = w;
int sz = 1;
for(int i = 0; i < v[id].size(); i++) {
if(w == -1) sz += dfs(v[id][i], i);
else sz += dfs(v[id][i], w);
df.push_back(nowid);
}
if(sz != 1) sz--;
return nsize[nowid] = sz;
}
void rmq() {
int n = df.size();
for(int i = 0; i < n; i++) {
dp[i][0] = df[i];
}
for(int j = 1; (1<<j) < n; j++) {
for(int i = 0; i + (1<<j) - 1 < n; i++) {
dp[i][j] = min(dp[i][j - 1], dp[i + (1<<(j - 1))][j - 1]);
}
}
}
int getans(int l, int r) {
int k = log2(r - l + 1.0005);
return min(dp[l][k], dp[r - (1<<k) + 1][k]);
}
4.艾教的线段树模板(区间修改,区间查询)
#include <iostream>
#define N 10000005
#define lson (id<<1)
#define rson ((id<<1)|1)
#define mid ((l+r)>>1)
using namespace std;
struct nod{
int sum,lazy;
}tree[N*5];
int n,q;
void push_up(int id)
{
tree[id].sum=tree[lson].sum+tree[rson].sum;
return;
}
void build_tree(int id,int l,int r)
{
// cout<<id<<' '<<l<<' '<<r<<' '<<mid<<endl;
if (l==r)
{
scanf("%d",&tree[id].sum);
tree[id].lazy=0;
return;
}
build_tree(lson,l,mid);
build_tree(rson,mid+1,r);
push_up(id);
tree[id].lazy=0;
return;
}
void push_down(int id,int l,int r)
{
tree[lson].sum+=(mid-l+1)*tree[id].lazy;
tree[lson].lazy+=tree[id].lazy;
tree[rson].sum+=(r-mid)*tree[id].lazy;
tree[rson].lazy+=tree[id].lazy;
tree[id].lazy=0;
return;
}
void ins(int id,int l,int r,int ql,int qr,int tt)
{
if (ql<=l && r<=qr)
{
tree[id].sum+=(r-l+1)*tt;
tree[id].lazy+=tt;
return;
}
if (tree[id].lazy) push_down(id,l,r);
if (ql<=mid)
ins(lson,l,mid,ql,qr,tt);
if (mid+1<=qr)
ins(rson,mid+1,r,ql,qr,tt);
push_up(id);
return;
}
int query(int id,int l,int r,int ql,int qr)
{
if (ql<=l && r<=qr)
{
return tree[id].sum;
}
if (tree[id].lazy)
push_down(id,l,r);
int sum=0;
if (ql<=mid)
sum+=query(lson,l,mid,ql,qr);
if (mid+1<=qr)
sum+=query(rson,mid+1,r,ql,qr);
return sum;
}
大数:支持加、减、乘(大数*int)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 10005;
ll SCALE = 1e9;
struct BigNum {
ll value[1005];
int len;
BigNum() {
memset(value, 0, sizeof value);
len = 1;
}
BigNum(ll x) {
memset(value, 0, sizeof value);
len = 0;
if(x == 0) len = 1;
while(x) {
value[len] = x%SCALE;
x /= SCALE;
len++;
}
}
};
BigNum operator + (const BigNum& t1, const BigNum& t2) {
ll jin = 0, he = 0;
BigNum ans;
ans.len = 0;
while(ans.len < t1.len && ans.len < t2.len) {
he = t1.value[ans.len] + t2.value[ans.len] + jin;
jin = he/SCALE;
ans.value[ans.len] = he%SCALE;
ans.len++;
}
while(ans.len < t1.len) {
he = t1.value[ans.len] + jin;
jin = he/SCALE;
ans.value[ans.len] = he%SCALE;
ans.len++;
}
while(ans.len < t2.len) {
he = t2.value[ans.len] + jin;
jin = he/SCALE;
ans.value[ans.len] = he%SCALE;
ans.len++;
}
if(jin != 0) {
ans.value[ans.len] = jin;
ans.len++;
}
return ans;
}
BigNum operator * (const BigNum& t1, const int& t2) {
BigNum ans;
ll jin = 0, t;
for(int i = 0; i < t1.len; i++) {
t = t1.value[i]*t2 + jin;
jin = t/SCALE;
ans.value[i] = t%SCALE;
}
ans.len = t1.len;
if(jin != 0) {
ans.value[ans.len] = jin;
ans.len++;
}
return ans;
}
BigNum operator - (const BigNum& t1, const BigNum& t2) {
BigNum ans;
ll jie = 0, t;
for(int i = 0; i < t2.len; i++) {
t = t2.value[i] + jie;
if(t > t1.value[i]) {
jie = 1;
ans.value[i] = SCALE + t1.value[i] - t;
}
else {
jie = 0;
ans.value[i] = t1.value[i] - t;
}
}
for(ans.len = t2.len; ans.len < t1.len; ans.len++) {
if(jie > t1.value[ans.len]) {
ans.value[ans.len] = SCALE + t1.value[ans.len] - jie;
jie = 1;
}
else {
ans.value[ans.len] = t1.value[ans.len] - jie;
jie = 0;
}
}
if(ans.value[ans.len - 1] == 0) ans.len--;
return ans;
}
void print(BigNum& ans) {
printf("%d", ans.value[ans.len - 1]);
for(int i = ans.len - 2; i >= 0; i--) {
printf("%09d", ans.value[i]);
}
puts("");
}
o1快速乘
inline long long multi(long long x,long long y,long long mod)
{
long long tmp=(x*y-(long long)((long double)x/mod*y+1.0e-8)*mod);
return tmp<0 ? tmp+mod : tmp;
}
日后再补充。。。