A
推个组合数式子就完事了。
C
树形dp。和题解做法一模一样。可能只有这一种做法吧。
D
每个数至多进行log次lowbit操作,然后lowbit操作就是乘2。线段树完事。
E
阅读理解半天给我整蒙了。直接6p=p+2p+3p。
I
签到。
J
百度搜索罗德里格旋转公式。我既不知道也不会手推,活该凉凉。
K
离线并查集。
L
- 巨麻烦,写了半天,我太菜了。
- 对第一个字符串的反串建造
。然后剩余
个字符串的反串在
上跑一跑,打个标记,就把公共子串解决了。
- 下面是第
小部分。容易发现按照字典序贪心遍历
树就能把字符串的反串的子串排序,这其实也是对反串建造
的初衷吧。原因就是字典图上的边是在字符串尾部添加字符,
树是在字符串首加字符,但是同一个节点的子串的后缀是相同,但前缀是不同的,因此要建立反串。有点绕,画出
的
理解下。记录每个节点代表的子串的字典序区间即可。
- 查询就直接在前缀和数组中二分查找即可。
- 注意多解输出最靠左的细节即可。
//感想:这个题需要对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
模拟。