A. Radishes

如果,我们可以进行暴力求解,复杂度

然而如果,根据抽屉原理,必定至少有一个出现了两边,此时。我们只要统计每一个数前两次的位置,将最多40个答案进行排序,即可。

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int w[100005],l[100005];
int main()
{
    int n;
    scanf("%d",&n);
    for (int i = 1; i <= n; i++) scanf("%d%d",&l[i],&w[i]);
    if (n > 40)
    {
        bool flag = true;
        for (int i = 1; i <= n && flag; i++)
            for (int j = i + 1; j <= n && flag; j++)
                if (l[i] == l[j])
                {
                    printf("%d %d\n",i,j);
                    flag = false; break;
                }
        return 0;
    }
    double minn = 1e200;
    int x1 = 0, x2 = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1 + i; j <= n; j++)
        {
            double tmp = max(1.0 * w[i] / w[j], 1.0 * w[j] / w[i]) * abs(l[i] - l[j]);
            if (tmp < minn)
            {
                minn = tmp;
                x1 = i; x2 = j;
            }
        }
    printf("%d %d\n",x1,x2);
    return 0;
}

B. Visual Cube

全场最简单?

首先算出来整个图形的大小,用字符串数组存图,先全部初始化为.,然后一条条边进行绘制。

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

char s[1005][1005];
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        int len=b*2+2*a+1;
        int height=2*b+2*c+1;
        memset(s,'.',sizeof(s));
        int now = -1;
        for (int i=1;i<=b;i++)
        {
            int nowl=2 * i - 1;
            now++;
            for (int j=1;j<=a;j++)
            {
                s[nowl][now+2*j-1]='+';
                s[nowl][now+2*j]='-';
            }
            s[nowl][now+2*a+1]='+';
            for (int j=1;j<=2*c;j++)
                s[2*i-1+j][now + 1]='|';
            nowl++; now++;
            for (int j=1;j<=a;j++)
            {
                s[nowl][now+2*j-1]='\\';
            }
            s[nowl][now+2*a+1]='\\';
        }
        int nowl=2*b;
        now++;
        for (int i=1;i<=c+1;i++)
        {
            nowl++;
            for (int j=1;j<=a;j++)
            {
                s[nowl][now + 2*j-1]='+';
                s[nowl][now + 2*j]='-';
            }
            s[nowl][now + 2*a+1]='+';
            int nowx=nowl,nowy=now + 1;
            for (int i=1;i<=2*b;i++)
            {
                nowx--; nowy--;
                if (s[nowx][nowy]=='.')
                    s[nowx][nowy]='\\';
                if (s[nowx][nowy]=='|') s[nowx][nowy]='+';
            }
            if (i==c+1) break;
            nowl++;
            for (int j=1;j<=a;j++)
            {
                s[nowl][now + 2*j-1]='|';
            }
            s[nowl][now + 2*a+1]='|';
        }
        for (int i=1;i<=height;i++)
        {
            for (int j=1;j<=len;j++) putchar(s[i][j]);
            putchar('\n');
        }
    }
    return 0;
}

C. Mr.Liang's Expression

dfs~注意前导0

#include <iostream>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <functional>
#include <map>
#include <set>
#include <bitset>
#include <ctime>
#include <complex>

const int INF = 0x3f3f3f3f;
const long long INFLL = 0x3f3f3f3f3f3f3f3fll;
#define memset0(x) memset(x, 0, sizeof(x))
#define memsetM1(x) memset(x, -1, sizeof(x))
#define memsetINF(x) memset(x, INF, sizeof(x))

using namespace std;

long long ans;
double num;
char str[15];
int ops[15];
int rk[5] = { 3, 1, 1, 2, 2 };

int opCmp(int a, int b)
{
    return rk[a] - rk[b];
}

void dfs(int i = 0, int op = 0)
{
    ops[i] = op;


    if (!str[i])
    {
        stack<double> numStack;
        stack<int> opStack;

        /*for (int j = 1; j < i; j++)
        {
            cout << ops[j] << " ";
        }
        cout << endl;*/

        numStack.push(str[0] - '0');
        for (int j = 1; j < i; j++)
        {
            int dig = str[j] - '0';

            if (ops[j] == 0)
            {
                numStack.push(dig);

                double num2 = numStack.top();
                numStack.pop();
                double num1 = numStack.top();
                numStack.pop();

                numStack.push(num1 * 10 + num2);
            }
            else
            {
                while (!opStack.empty() && opCmp(opStack.top(), ops[j]) > 0)
                {
                    double num2 = numStack.top();
                    numStack.pop();
                    double num1 = numStack.top();
                    numStack.pop();
                    switch (opStack.top())
                    {
                    case 0:
                        num1 = num1 * 10 + num2;
                        break;
                    case 1:
                        num1 = num1 + num2;
                        break;
                    case 2:
                        num1 = num1 - num2;
                        break;
                    case 3:
                        num1 = num1 * num2;
                        break;
                    }
                    numStack.push(num1);

                    opStack.pop();
                }
                opStack.push(ops[j]);
                numStack.push(dig);
            }

        }
        while (!opStack.empty())
        {
            int cOp = opStack.top();
            opStack.pop();
            double num2 = numStack.top();
            numStack.pop();
            double num1 = numStack.top();
            numStack.pop();
            switch (cOp)
            {
            case 0:
                num1 = num1 * 10 + num2;
                break;
            case 1:
                num1 = num1 + num2;
                break;
            case 2:
                num1 = num1 - num2;
                break;
            case 3:
                num1 = num1 * num2;
                break;
            }
            numStack.push(num1);
        }
        //cout << numStack.top() << endl;
        if (abs(numStack.top() - num) < 1e-6)
        {
            ans++;
        }
        numStack.pop();
        return;
    }
    if (i >= 1 && op == 0)
    {
        if (str[i - 1] == '0' && (i == 1 || ops[i - 1] != 0))
        {
            return;
        }
    }

    int dig = str[i] - '0';
    if (!str[i + 1])
    {
        dfs(i + 1, 0);
    }
    else
    {
        for (int j = 0; j < 4; j++)
        {
            dfs(i + 1, j);
        }
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
    int startTime = clock();
#endif
    cin >> str >> num;
    dfs();
    cout << ans << endl;


#ifndef ONLINE_JUDGE
    printf("Time = %dms\n", clock() - startTime);
#endif
    return 0;
}

D. Xiao Ming's String

首先如果一个字母的出现次数,显然不能,则输出NO

否则使用优先队列,每次取前两个元素进行交叉。

// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <ctime>
#include <vector>
#include <fstream>
#include <list>
#include <iomanip>
#include <numeric>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define ms(s) memset(s, 0, sizeof(s))
const int inf = 0x3f3f3f3f;
#define LOCAL
const int maxn = 1e6 + 7;
struct alpha{
    int cnt;
    char c;
    alpha(){}
    alpha(int _cnt, char _c){
        cnt = _cnt;
        c = _c;
    }
    bool operator < (const alpha &other) const{
        return cnt < other.cnt;
    }
};

int main(int argc, char * argv[]) 
{
    string s, ans;
    cin >> s;
    int mark[maxn];
    priority_queue<alpha> q;
    bool is_ok = true;
    memset(mark, 0, sizeof(mark));
    for (int i = 0; i < s.size(); i++){
        mark[s[i] - 'a']++;
    }
    for (int i = 0; i < 26; i++){
        if (mark[i]){
            q.push(alpha(mark[i], char(i + 'a')));
        }
    }
    while (!q.empty()){
        alpha a = q.top();
        q.pop();
        if(a.cnt == 0){
            break;
        }
        ans.push_back(char(a.c));
        a.cnt--;

        alpha b = q.top();
        q.pop();
        if (b.cnt == 0){
            break;
        }
        ans.push_back(char(b.c));
        b.cnt--;
        q.push(a);
        q.push(b);
    }
    for (int i = 1; i < ans.size(); i++){
        if (ans[i] == ans[i - 1]){
            is_ok = false;
            break;
        }
    }
    if (is_ok && ans.size() == s.size()){
        cout << ans << endl;
    }
    else{
        cout << "NO" << endl;
    }
    return 0;
}

E. Mr.Liang's Sequence Problem(I)

找找因数,推推式子,注意使用long long,就完事了~

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

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        long long N;
        scanf("%lld",&N);
        long long count = 1, sub = sqrt(2 * N);
        for(long long k = 2; k <= sub; k++) {
            if ((N - (k * (k - 1) / 2)) % k == 0) count++;
        }
        printf("%lld\n",count);
    }
    return 0;
}

F. Mr.Liang's Sequence Problem(II)

This IS a template problem for segment tree CDQ.

Solution A: CDQ

假设: 为原数组第项, 前缀和, 差分

区间查询的和, 实际上可以拆分为个前缀和的差

区间修改的值, 实际上可以表示为个差分数组的变化

考虑差分数组对前缀和数组第的影响

所以只需要维护好所有的和, 便可以的求出

所以按照分治的经典套路, 第一维对时间分治, 第二维按照修改/查询的位置(拆分为个单点修改/查询)进行排序, 考虑分治的左边对右边影响的时候按照上面的做法便可以的进行修改/查询, 复杂度:

#include<bits/stdc++.h>
#define fi first
#define sf scanf
#define se second
#define pf printf
#define pb push_back
#define mp make_pair
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(x,y) memset((x),(y),sizeof(x))
#define fup(i,x,y) for(int i=(x);i<=(y);++i)
#define fdn(i,x,y) for(int i=(x);i>=(y);--i)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<int,int> pii;
using namespace std;

const int __=4e5+5;

struct node
{
    int id;ull p,v;
}a[__],t[__];

ull ans[__];

void cdq(int l,int r)
{
    if(l==r)return;
    int m=(l+r)>>1;
    cdq(l,m),cdq(m+1,r);
    ull sum[2]={0};int x,y,z=l;
    for(x=l,y=m+1;y<=r;++y)
    {
        for(;x<=m && a[x].p<=a[y].p;t[z++]=a[x++])
            if(!a[x].id)
            {
                sum[0]=sum[0]+a[x].v;
                sum[1]=sum[1]+a[x].v*a[x].p;
            }
        if(a[y].id)
            ans[a[y].id]+=a[y].v*((a[y].p+1)*sum[0]-sum[1]);
        t[z++]=a[y];
    }
    for(int i=x;i<=m;++i)t[z++]=a[i];
    fup(i,l,r)a[i]=t[i];
}

int main()
{
    ll n;int q;sf("%lld%d",&n,&q);
    int idx=0,qdx=0;
    while(q--)
    {
        int op;ull l,r;
        sf("%d%llu%llu",&op,&l,&r);
        if(op==1)
        {
            ull v;sf("%llu",&v);
            a[++idx]={0,l,v};
            a[++idx]={0,r+1,-v};
        }
        else
        {
            ++qdx;
            a[++idx]={qdx,r,1};
            a[++idx]={qdx,l-1,-1ull};
        }
    }
    cdq(1,idx);
    fup(i,1,qdx)
        pf("%llu\n",ans[i]);
    return 0;
}

Solution B: Segment Tree

当然良心的懋懋把这题实现开到了1.5s,线段树可以通过

离散化后线段树, 注意线段树离散化后是个点(每个线段包含个端点)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+5;
typedef long long ll;
int Case = 1;
vector<unsigned long long>ve;
struct node{
    int op;
    long long l, r;
    unsigned long long val;
}Q[maxn];
struct point{
    int l, r;
    unsigned long long len;
    unsigned long long sum;
    unsigned long long lazy;
    int mid() {
        return (l+r)/2;
    }
}tr[maxn<<2];
void pushup(int rt) {
    tr[rt].len = tr[rt<<1].len + tr[rt<<1|1].len;
    tr[rt].sum = tr[rt<<1].sum + tr[rt<<1|1].sum;
}
void build(int rt, int l, int r) {
    tr[rt].l = l;tr[rt].r = r;
    if(l == r) {
        tr[rt].len = ve[l]-ve[l-1];
        return;
    }
    int mid = tr[rt].mid();
    build(rt<<1, l, mid);
    build(rt<<1|1, mid+1, r);
    pushup(rt);
}
void pushdown(int rt) {
    if(tr[rt].lazy) {
        unsigned long long x = tr[rt].lazy;
        tr[rt<<1].lazy += x;
        tr[rt<<1|1].lazy += x;
        tr[rt<<1].sum += x*tr[rt<<1].len;
        tr[rt<<1|1].sum += x*tr[rt<<1|1].len;
        tr[rt].lazy = 0;
    }
}
void update(int rt, int l, int r, unsigned long long val) {
    //cout<<l<<" "<<r<<endl;
    if(tr[rt].l == l && tr[rt]. r == r) {
        tr[rt].lazy += val;
        tr[rt].sum += val*tr[rt].len;
        return;
    }
    pushdown(rt);
    int mid = tr[rt].mid();
    if(r <= mid) update(rt<<1, l, r, val);
    else if(l > mid) update(rt<<1|1, l, r, val);
    else {
        update(rt<<1, l, mid, val);
        update(rt<<1|1, mid+1, r, val);
    }
    pushup(rt);
}
unsigned long long query(int rt, int l, int r) {
    //cout<<l<<" "<<r<<endl;
    if(tr[rt].l == l && tr[rt].r == r) {
        return tr[rt].sum;
    }
    pushdown(rt);
    int mid = tr[rt].mid();
    if (r <= mid)
      return query(rt << 1, l, r);
    else if (l > mid)
      return query(rt << 1|1, l, r);
    else
      return query(rt << 1, l, mid) + query(rt << 1|1, mid+1, r);
}
int getid(long long x) {
    return lower_bound(ve.begin(), ve.end(), x)-ve.begin()+1;
}
void solve() {
    //freopen("in.txt", "r", stdin);
    long long n;int q;
    scanf("%lld%d", &n, &q);
    for(int i = 1; i <= q; i++) {
        scanf("%d", &Q[i].op);
        if(Q[i].op == 1) {
            scanf("%lld%lld%llu", &Q[i].l, &Q[i].r, &Q[i].val);
        }
        else scanf("%lld%lld", &Q[i].l, &Q[i].r);
        Q[i].r++;
        ve.push_back(Q[i].l);
        ve.push_back(Q[i].r);
    }
    sort(ve.begin(), ve.end());
    ve.erase(unique(ve.begin(), ve.end()), ve.end());
    int len = ve.size();
    build(1, 1, len-1);
    for(int i = 1; i <= q; i++) {
        long long l = Q[i].l, r = Q[i].r;
        int op = Q[i].op;
        if(op == 1) update(1, getid(l), getid(r)-1, Q[i].val);
        else
          printf("%llu\n", query(1, getid(l), getid(r)-1));
    }
    return;
}
int main() {
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
while(Case--) {
    solve();
    }
return 0;
}

G. q_Wq's Dating

显然要求两个人手上的串一样也就是这个串是回文串

树上的回文子串查询问题无法使用类似于 Mancher 的算法解决。

考虑哈希的解法。首先假定节点为根,将无根树转换为有根树。对于树上每个节点,预处理维护两个哈希值:表示从节点到节点的路径上的字母序列的哈希值,表示从节点到节点的路径上的字母序列的哈希值。则树上任意一有向简单路的哈希值均可计算。处理查询时,只需要检查从的哈希值是否与从的哈希值相同即可。

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

const int MAXN=400005;
const int mod=998244353;
struct node{
    int id;
    long long pre,en;
    int prelen,enlen;
    vector<int> G;
}mapp[MAXN];
char s[MAXN];
int degree[MAXN];
long long pp[MAXN],pq[MAXN];
int depth[MAXN];
int parent[MAXN][25];

void dfs(int x, int y)
{
    depth[x] = depth[y] + 1;
    parent[x][0] = y;
    for (auto &i: mapp[x].G)
        if (i != y) dfs(i, x);
}
void build(int root, int n)
{
    dfs(root, 0);
    for (int i = 1; i <= 20; i++)
        for (int j = 1; j <= n; j++)
            parent[j][i] = parent[parent[j][i - 1]][i - 1];
}
int lca(int u, int v)
{
    for (int i = 20; i >= 0; i--)
    {
        if (depth[parent[u][i]] >= depth[v])
            u = parent[u][i];
        if (depth[parent[v][i]] >= depth[u])
            v = parent[v][i];
    }
    for (int i = 20; i >= 0; i--)
        if (parent[u][i] != parent[v][i])
        {
            u = parent[u][i];
            v = parent[v][i];
        }
    if (u != v)
    {
        u = parent[u][0];
        v = parent[v][0];
    }
    return u;
}
void dfs1(int now,int last)
{
    mapp[now].pre=(mapp[last].pre*1001+s[now])%mod;
    mapp[now].prelen=mapp[last].prelen+1;
    for (auto &i:mapp[now].G)
        if (i!=last)
            dfs1(i,now);
}
long long qpow(long long base, long long index) {
    long long ans = 1;
    while (index) {
        if (index & 1)
            ans = ans * base % mod;
        base = base * base % mod;
        index >>= 1;
    }
    return ans;
}
void dfs2(int now,int last)
{
    if (now==1)
    {
        mapp[1].en=s[1];
        mapp[1].enlen=1;
        return;
    }
    for (auto &i:mapp[now].G)
        if (i!=last&&mapp[i].prelen<mapp[now].prelen)
        {
            dfs2(i,now);
            mapp[now].enlen=mapp[i].enlen+1;
            mapp[now].en=(mapp[i].en+s[now]*pp[mapp[i].enlen])%mod;
        }
}
inline long long gethash(int u,int v)
{
    int t=lca(u,v);
    long long tmp1=((mapp[u].en-mapp[t].en)%mod+mod)%mod*pq[mapp[t].enlen]%mod;
    long long tmp2=((mapp[v].pre-mapp[t].pre*pp[mapp[v].prelen-mapp[t].prelen]%mod)%mod+mod)%mod;
    long long ans=((tmp1*1001+s[t])%mod*pp[mapp[v].prelen-mapp[t].prelen]%mod+tmp2)%mod;
    //printf("%d %d %d %lld %lld %lld\n",u,v,t,tmp1,tmp2,ans);
    return ans;
}
int main()
{
    mapp[1].pre=mapp[1].en=mapp[1].prelen=mapp[1].enlen=0;
    int n;
    scanf("%d",&n);
    pp[0]=1; pp[1]=1001;
    for (int i=2;i<=n;i++) pp[i]=pp[i-1]*1001%mod;
    pq[0]=1; pq[1]=qpow(1001,mod-2);
    for (int i=2;i<=n;i++) pq[i]=pq[i-1]*pq[1]%mod;
    scanf("%s",s+1);
    for (int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        mapp[u].G.emplace_back(v);
        mapp[v].G.emplace_back(u);
        degree[u]++; degree[v]++;
    }
    build(1,n);
    dfs1(1,0);
    for (int i=2;i<=n;i++)
        if (degree[i]==1)
            dfs2(i,0);
    int Q;
    scanf("%d",&Q);
    while (Q--)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if (gethash(u,v)==gethash(v,u))
            printf("Happy!\n");
        else printf("555\n");
    }
    return 0;
}

H. Mr.Liang's Sequence Problem(III)

不难打表答案就是

在不打表()之前,这题是这样做的:

定义: 中不同数字的个数, 那么有一个比较经典的结论:

可以观察到: 若, 那么, 因此关于满足单调性, 所以可以进行二分, 找到的最小值以及的最小值, 答案就是:

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

int main()
{
    int T;
    scanf("%d",&T);
    while (T--){
        long long x;
        scanf("%lld",&x);
        printf("%lld\n",x / 2 + 1);
    }
    return 0;
}

I. SuPer Fast Algorithm

不难发现,这是一个SPFA的代码,需要你hack它,也就是你要使得他TLE。

  1. 打开百度
  2. 搜索SPFA怎么卡
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <bitset>
#include <complex>
#include <random>
#include <stack>
#include <time.h>
#define SET0(X) memset(X,0,sizeof(X));
#define SET_1(X) memset(X,-1,sizeof(X));
#define RAND(a,b) ((rand() % (b-a+1))+ a)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
struct edge{int u,v,w;};
vector<edge>v;


int id[20][10010],n=10,cnt,m=100000/n,a[1000000];
int r(){
    return rand();
}
int main(){
    srand((unsigned)time(NULL));
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            id[i][j]=++cnt;
            a[cnt]=cnt;
        }

    int SIZE=990;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            if(i<n){
                v.push_back(edge{id[i][j],id[i+1][j],1});
                if(j<m){
                    v.push_back(edge{id[i][j],id[i+1][j+1],r()%SIZE+10});
                    v.push_back(edge{id[i+1][j+1],id[i][j],r()%SIZE+10});
                }
            }
            if(j<m){
                v.push_back(edge{id[i][j],id[i][j+1],r()%SIZE+10});
            }
        }
    random_shuffle(v.begin(),v.end());
    printf("%d %d\n",cnt,(int)v.size());
    for(int i=0;i<v.size();++i)
        printf("%d %d %d\n",a[v[i].u],a[v[i].v],v[i].w);
    return 0;
}

J. Regular Expression

对所有的字符串建立AC自动机, 由于,那么AC自动机上节点数

代表: 考虑字符串个字符, 在自动机上状态为的答案

,则为状态读入字符后转移的状态。

,则为状态读入字符后转移的状态。

为以状态结尾的单词的个数

第一维可以使用滚动数组优化掉。

#include <bits/stdc++.h>
#define fi first
#define se second
#define sf scanf
#define pf printf
#define pb push_back
#define mp make_pair
#define sz(x) ((int)(x).size())
#define fo(i,a) for(int i=1;i<=(a.n);i++)
#define mem(x,y) memset((x),(y),sizeof(x))
#define fup(i,x,y) for(int i=(x);i<=(y);i++)
#define fdn(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

const int __=1e3+5;

int dp[2][__];

struct AhoCorasickAutomaton
{
    static const int alp=26;

    static int to_idx(char ch)
    {
        return ch-'a'+1;
    }

    struct Trie
    {
        struct node
        {
            int nex[alp+1],last,num;
            void clear()
            {
                num=last=0;
                mem(nex,0);
            }
        }t[__];

        Trie() {clear();}

        node& operator[](int x){return t[x];}

        int idx;

        void insert(char s[])
        {
            int x=0;
            for(int i=1;s[i];++i)
            {
                int k=to_idx(s[i]);
                if(!t[x].nex[k])
                {
                    t[x].nex[k]=++idx;
                    t[idx].clear();
                }
                x=t[x].nex[k];
            }
            ++t[x].num;
        }

        void clear(){idx=0;t[0].clear();}
    }t;

    AhoCorasickAutomaton() {clear();}

#define nex(x) t[x].nex[i]
#define fail(x) t[x].nex[0]

    void get_fail()
    {
        queue<int>Q;Q.push(0);
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=1;i<=alp;++i)
                if(nex(x))
                {
                    Q.push(nex(x));
                    if(x)fail(nex(x))=nex(fail(x));
                }
                else nex(x)=nex(fail(x));
            if(t[fail(x)].num)t[x].last=fail(x);
            else t[x].last=t[fail(x)].last;
            t[x].num+=t[t[x].last].num;
        }
    }

    int ac(char s[],int len)
    {
        dp[0][0]=0;
        for(int i=1,f=1;i<=len;++i,f=!f)
        {
            mem(dp[f],-1);
            for(int j=0;j<=t.idx;++j)
            {
                if(dp[!f][j]==-1)continue;
                if(s[i]!='?')
                {
                    int x=to_idx(s[i]),y=t[j].nex[x];
                    dp[f][y]=max(dp[f][y],dp[!f][j]+t[y].num);
                    continue;
                }
                for(int k=1;k<=alp;++k)
                {
                    int y=t[j].nex[k];
                    dp[f][y]=max(dp[f][y],dp[!f][j]+t[y].num);
                }
            }
        }
        int ans=0;
        for(int i=0;i<=t.idx;++i)
            ans=max(ans,dp[len&1][i]);
        return ans;
    }

    void clear(){t.clear();}
}aca;

char a[__],b[__];

int main()
{
    mem(dp,-1);
    int m;sf("%s%d",a+1,&m);
    for(int i=1;i<=m;++i)
    {
        sf("%s",b+1);
        aca.t.insert(b);
    }
    aca.get_fail();
    pf("%d\n",aca.ac(a,strlen(a+1)));
    return 0;
}

K. Mr.Liang's Sequence Problem(IV)

1.数位

数位简单题, 可能被发现的比较晚

代表长度为, 数位总和为的所有数字的数位之和

#include<bits/stdc++.h>
#define fi first
#define sf scanf
#define se second
#define pf printf
#define pb push_back
#define mp make_pair
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(x,y) memset((x),(y),sizeof(x))
#define fup(i,x,y) for(int i=(x);i<=(y);++i)
#define fdn(i,x,y) for(int i=(x);i>=(y);--i)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<int,int> pii;
using namespace std;

struct DigitalDynamicProgramming
{
    int bit[20];
    ll dp[20][200];

    DigitalDynamicProgramming() {memset(dp,-1,sizeof(dp));}

    ll dfs(int len,int sum,bool lim)
    {
        if(!len)return sum;
        if(!lim && ~dp[len][sum])return dp[len][sum];
        int r=lim?bit[len]:9;

        ll res=0;
        for(int i=0;i<=r;++i)
        {
            res+=dfs(len-1,sum+i,lim && i==bit[len]);
        }
        if(lim)return res;
        return dp[len][sum]=res;
    }

    ll cal(ll x)
    {
        if(x<0)return 0;
        int idx=0;
        for(;x;x/=10)bit[++idx]=x%10;
        return dfs(idx,0,true);
    }
}dp;

int main()
{
    ll l,r;sf("%lld%lld",&l,&r);
    pf("%lld\n",dp.cal(r)-dp.cal(l-1));
    return 0;
}

2.计算贡献

考虑数字分别在个位, 十位, 百位,出现的次数, 不难发现:

个位每个数字出现一个循环, 每个循环中每个数字分别出现

百位每个数字出现一个循环, 每个循环中每个数字分别出现连续

千位每个数字出现一个循环, 每个循环中每个数字分别出现连续

以此类推

#include<bits/stdc++.h>
#define fi first
#define sf scanf
#define se second
#define pf printf
#define pb push_back
#define mp make_pair
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(x,y) memset((x),(y),sizeof(x))
#define fup(i,x,y) for(int i=(x);i<=(y);++i)
#define fdn(i,x,y) for(int i=(x);i>=(y);--i)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<int,int> pii;
using namespace std;

ll cal(ll n)
{
    ll res=0;
    for(ll m=10;m<=n*10;m*=10)
        for(int i=0;i<=9;++i)
        {
            res+=n/m*(m/10)*i;
            if(n%m+1>=i*m/10)
                res+=min(n%m+1-i*m/10,m/10)*i;
        }
    return res;
}

int main()
{
    ll l,r;sf("%lld%lld",&l,&r);
    pf("%lld\n",cal(r)-cal(l-1));
    return 0;
}