2021东北四省赛部分题解

链接

A

推个组合数式子就完事了。

C

树形dp。和题解做法一模一样。可能只有这一种做法吧。

D

每个数至多进行log次lowbit操作,然后lowbit操作就是乘2。线段树完事。

E

阅读理解半天给我整蒙了。直接6p=p+2p+3p。

I

签到。

J

百度搜索罗德里格旋转公式。我既不知道也不会手推,活该凉凉。

K

离线并查集。

L

  • 巨麻烦,写了半天,我太菜了。
  • 对第一个字符串的反串建造sam。然后剩余n-1个字符串的反串在sam上跑一跑,打个标记,就把公共子串解决了。
  • 下面是第k小部分。容易发现按照字典序贪心遍历fail树就能把字符串的反串的子串排序,这其实也是对反串建造sam的初衷吧。原因就是字典图上的边是在字符串尾部添加字符, fail树是在字符串首加字符,但是同一个节点的子串的后缀是相同,但前缀是不同的,因此要建立反串。有点绕,画出abbsam理解下。记录每个节点代表的子串的字典序区间即可。
  • 查询就直接在前缀和数组中二分查找即可。
  • 注意多解输出最靠左的细节即可。

//感想:这个题需要对sam有比较透彻的理解才能快速写完
#include<bits/stdc++.h>
using namespace std ;
struct Sam
{
    string s ;
    int last , cnt ;
    long long x ;
    vector<vector<int>> tr ;
    vector<int> nxt ;
    vector<int> len ;
    vector<int> in ;
    queue<int> q ;
    vector<int> pos ;
    vector<int> num ;
    vector<pair<long long , int>> res ; //u , L
    vector<vector<int>> g ;
    vector<int> p ;
    vector<int> mx ;
    vector<int> label ;
    void init()
    {
        last = cnt = 1 ;
        int up1 = s.size() * 2 + 10 ;
        int up2 = 30 ; //字符集大小
        tr.resize(0) ;
        tr.resize(up1 , vector<int>(up2 , 0)) ;
        nxt.resize(0) ;
        nxt.resize(up1 , 0) ;
        len.resize(0) ;
        len.resize(up1 , 0) ;
        in.resize(0) ;
        in.resize(up1 , 0) ;
        pos.resize(0) ;
        pos.resize(up1 , -1) ;
        num.resize(0) ;
        num.resize(up1 , 0) ;
        res.resize(0) ;
        g.resize(0) ;
        g.resize(up1 , vector<int>(up2 , 0)) ;
        p.resize(0) ;
        p.resize(up1 , 1000000) ;
        label.resize(0) ;
        label.resize(up1 , 0) ;
        mx.resize(0) ;
        mx.resize(up1 , 0) ;
        while(!q.empty())  q.pop() ;
        x = 0 ;
    }
    void add(int c , int id)
    {
        int p = last , np = ++ cnt ;
        pos[np] = id ;
        last = np , len[np] = len[p] + 1 ;
        for( ; p && !tr[p][c] ; p = nxt[p])  tr[p][c] = np ;
        if(!p)  nxt[np] = 1 ;
        else
        {
            int q = tr[p][c] ;
            if(len[p] + 1 == len[q])  nxt[np] = q ;
            else
            {
                int nq = ++ cnt ;
                pos[nq] = id ;
                len[nq] = len[p] + 1 ;
                tr[nq].assign(tr[q].begin() , tr[q].end()) ;
                nxt[nq] = nxt[q] , nxt[q] = nxt[np] = nq ;
                for( ; tr[p][c] == q ; p = nxt[p])  tr[p][c] = nq ;
            }
        }
    }
    void build()
    {
        int t = s.size() ;
        for(int i = 0 ; i <= t - 1 ; i ++)  add(s[i] - 'a' , i) ;
        for(int i = 1 ; i <= cnt ; i ++)  p[i] = len[i] ;

    }
    void mark(string s , int t)
    {
        int now = 1 ;
        int y = 0 ;
        vector<int> v ;
        for(int i = 0 ; i < (int)s.size() ; i ++)
        {
            int c = s[i] - 'a' ;
            while(tr[now][c] == 0 && now > 1)  now = nxt[now] , y = len[now] ;
            if(tr[now][c] > 1)
            {
                now = tr[now][c] ;
                y ++ ;
                mx[now] = max(mx[now] , y) ;
                if(label[now] != t)
                {
                    label[now] = t ;
                    v.push_back(now) ;
                    int d = now ;
                    while(nxt[d] > 1 && label[nxt[d]] != t)
                    {
                        d = nxt[d] ;
                        label[d] = t ;
                        v.push_back(d) ;
                    }
                }
            }
        }
        sort(v.begin() , v.end() , [&](int a , int b)
            {return len[a] > len[b] ;}
        ) ;
        for(auto c : v)
        {
            num[c] ++ ;
            p[c] = min(p[c] , mx[c]) ;
            mx[nxt[c]] = max(mx[nxt[c]] , mx[c]) ;
            mx[c] = 0 ;
        }
    }
    void build_tree(int t)
    {
        // for(int i = 1 ; i <= cnt ; i ++)  cout << i << ' ' << p[i] << '\n' ;
        // for(int i = 1 ; i <= cnt ; i ++)
        //     for(int j = 0 ; j < 26 ; j ++)
        //         if(tr[i][j] > 1)
        //             cout << "??? " << i << ' ' << j << ' ' << tr[i][j] << '\n' ;
        for(int i = 1 ; i <= cnt ; i ++)  in[nxt[i]] ++ ;
        for(int i = 1 ; i <= cnt ; i ++)  if(in[i] == 0)  q.push(i) ; 
        while(!q.empty())
        {
            int u = q.front() ;
            q.pop() ;
            pos[nxt[u]] = max(pos[nxt[u]] , pos[u]) ;
            if(nxt[u] >= 1)
            {
                int i = pos[u] - len[nxt[u]] ;
                int c = s[i] - 'a' ;
                g[nxt[u]][c] = u ;
            }
            in[nxt[u]] -- ;
            if(in[nxt[u]] == 0)  q.push(nxt[u]) ;
        }
    }
    void dfs(int u , int t)
    {
        for(int i = 0 ; i < 26 ; i ++)
        {
            int v = g[u][i] ;
            if(v > 0)
            {
                if(num[v] == t)
                {
                    res.push_back({x + 1 , v}) ;
                    x += p[v] - len[nxt[v]] ;
                }
                dfs(v , t) ;   
            }
        }
    }
    void cal(int t)
    {
        dfs(1 , t) ;
    }
    int rev(int x)
    {
        return s.size() - 1 - x ;
    }
    void solve(int k)
    {
        if(k > x)
        {
            cout << "-1\n" ;
            return ;
        }
        auto it = upper_bound(res.begin() , res.end() , pair<long long , int>{k , 1000000000}) ;
        it -- ;
        int L = (*it).first ;
        int u = (*it).second ;
        int id = k - L + 1 ;
        int ans1 = pos[u] - len[nxt[u]] - id + 1 ;
        int ans2 = pos[u] ;
        cout << rev(ans2) << ' ' << rev(ans1) + 1 << '\n' ;
    }
} sam ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n ;
        cin >> n ;
        cin >> sam.s ;
        reverse(sam.s.begin() , sam.s.end()) ;
        sam.init() ;
        sam.build() ;
        for(int i = 2 ; i <= n ; i ++)
        {
            string s ;
            cin >> s ;
            reverse(s.begin() , s.end()) ;
            sam.mark(s , i - 1) ;
        }
        sam.build_tree(n - 1) ;
        sam.cal(n - 1) ;
        int q ;
        cin >> q ;
        while(q --)
        {
            int k ;
            cin >> k ;
            sam.solve(k) ;
        }
    }
    return 0 ;
    //you should actually read the stuff at the bottom before submitting or in the confusion
}
/* stuff you should look for
    * long long
    * array bounds
    * init
    * ios
    * special cases (n=1?)
    * do smth instead of nothing and stay organized
    * WRITE STUFF DOWN
    * DON'T GET STUCK ON ONE APPROACH
    * DON'T GET STUCK ON ONE PROBLEM
*/

M

模拟。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值