题目: meeting
链接:https://ac.nowcoder.com/acm/contest/884/A
大意:从一棵树(n=1e5)上,选一个点,使这个点到其他点的距离的最大值最小
分析:
2种做法:
#include
#define F(i,a,b) for(ll i=(a);i<=ll(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(1e5+9);
struct edge
{
int f, t;
};
struct info
{
int f, t;
int len;
bool operator<(const info&ri)const
{
return len > ri.len;
}
};
vector<info>H[maxn];
vector<edge>G[maxn];
int in[maxn], k, ans[maxn];
void upd(int from,int to)
{
int ma = -inf;
int cnt(0);
F(i, 0, H[from].size() - 1)if (H[from][i].f != to)
{
++cnt;
if (cnt == 1)
{
ma = H[from][i].len+1;
break;
}
}
if (ma != -inf)
{
H[to].push_back({ from,to,ma });
sort(H[to].begin(), H[to].end());
if (H[to].size() >= 3)H[to].pop_back();
}
}
int dfs1(int u,int p)
{
if (in[u])
{
H[u].push_back({ u,u,0 });
}
for (auto &e : G[u])if(e.t!=p)
{
int v = e.t;
dfs1(v,u);
}
//sort(H[u].begin(), H[u].end());
upd(u, p);
return 1;
}
int dfs2(int u, int p)
{
if(u!=1)upd(p,u);
//sort(H[u].begin(), H[u].end());
if (H[u].size())ans[u] = H[u][0].len;
else
{
ans[u] = 0;
assert(0);
}
for (auto &e : G[u])if(e.t!=p)
{
int v = e.t;
dfs2(v, u);
}
return 1;
}
int n;
template<typename INint> inline void IN(INint &x)
{
x = 0; int f = 1; char c; cin.get(c);
while (c<'0' || c>'9') { if (c == '-')f = -1; cin.get(c); }
while (c >= '0'&&c <= '9') { x = x * 10 + c - '0'; cin.get(c); }
x *= f;
}
#define endl '\n'
int main()
{
#ifndef endl
freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
#endif // !endl
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T(1), cas(0);
//cin >> T;
while (cas, T--)
{
cin >> n >> k;
//F(i, 0, n)G[i].clear(), H[i].clear(),in[i]=0;
F(i, 1, n - 1)
{
int f, t;
//cin >> f >> t;
IN(f); IN(t);
G[f].push_back({ f,t });
G[t].push_back({ t,f });
}
F(i, 1, k) { int p; /*cin >> p;*/IN(p); in[p] = 1; }
if (k == 0)
{
cout << 0 << '\n';
continue;
}
dfs1(1, 0);
dfs2(1, 0);
int val = *min_element(ans + 1, ans + 1 + n);
cout << val << '\n';
}
return 0;
}
``
题目:xor
链接:https://ac.nowcoder.com/acm/contest/884/B
大意: 给出1~n(n<5e4)共n个集合,每个集合最多由32个数字组成
q<=5e4次询问,问数字x是否可有[le,ri]中的任意一个集合中的数字异或得到
分析:知道了之后这就是一个,模板题,线性基可以求并,并之后的线性基的可以得到的数是两个线性基可以一起得到的数。
线性基还可以求交,交之后的线性基的可以得到的数是两个线性基都可以得到的数
代码
#include
#define F(i,a,b) for(int i=(a);i<=ll(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(5e4 + 8);
//3_32-线性基
const int maxbit = 35;
struct L_B {
ll b[maxbit];
int cnt_lose;
int cnt;//不rebuild为0,rebuild了才是个数
L_B()
{
memset(b, 0, sizeof(b));
//memset(rk, 0, sizeof(rk));
cnt = cnt_lose = 0;
}
bool insert(ll val)
{
for (int i = maxbit - 1; i >= 0; i--)if (val&((ll)1 << i))
{
if (!b[i]) { b[i] = val; break; }
else val ^= b[i];
}
val > 0 ? ++cnt : ++cnt_lose;
return val > 0;
}
bool check(ll val)
{
for (int i = maxbit - 1; i >= 0; i--)if (val&((ll)1 << i))
{
if (!b[i]) { break; }
else val ^= b[i];
}
val > 0 ? ++cnt : ++cnt_lose;
return val > 0;
}
L_B inter(const L_B &ri)
{
L_B &le = *this;
L_B ALL = le, C, D;
for (int i = maxbit - 1; i >= 0; --i)if (ri.b[i])
{
ll v = ri.b[i], k = ll(1) << i;
bool ok = 1;
for (int j = maxbit - 1; j >= 0; --j)if (v&(ll(1) << j))
{
if (ALL.b[j]) v ^= ALL.b[j], k ^= D.b[j];
else
{
ok = 0, ALL.b[j] = v, D.b[j] = k;
break;
}
}
if (ok)
{
ll v = 0;
for (int j = maxbit - 1; j >= 0; --j)if (k&(ll(1) << j))
v ^= ri.b[j];
C.insert(v);
}
}
return C;
}
}lbs;
namespace fastIO {
#define BUF_SIZE 100000
//fread -> read
bool IOerror = 0;
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if (p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if (pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
inline void read(ll &x) {
char ch;
while (blank(ch = nc()));
if (IOerror) return;
for (x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
#undef BUF_SIZE
};
using namespace fastIO;
vector<ll>G[maxn];
//6_2-线段树
//线段树模板,例子是区间求和+权值求和+最值
#define ls (o<<1)
#define rs (o<<1|1)
#define ndp o,ndl,ndr
#define ndmid ((ndl+ndr)>>1)
#define lsp ls,ndl,ndmid
#define rsp rs,(ndmid+1),ndr
#define ndpf int o,int ndl,int ndr
struct node
{
L_B lbs;
};
struct set_T
{
node es[maxn << 2];
void comb(node&it, const node&le, const node&ri)//TODO
{
it = { le.lbs };
it.lbs=it.lbs.inter(ri.lbs);
}
node getcomb(const node&le, const node&ri)//TODO
{
node ret = { le.lbs };
ret.lbs.inter(ri.lbs);
return ret;
}
void PushUp(int o) { comb(es[o], es[ls], es[rs]); }
void build(int o, int ndl, int ndr)//TODO:自定义初始化数据
{
if (ndl == ndr)
{ /*es[o].lbs = L_B();*/
for (auto&v : G[ndl])es[o].lbs.insert(v);
es[o].lbs.rebuild();
return;
}
build(lsp); build(rsp);
PushUp(o);
}
void prin(ndpf)//TODO:自定义打印信息
{
cout << o << ":[" << ndl << "," << ndr << "]:";
if (ndl == ndr)return;
prin(lsp); prin(rsp);
}
bool query(ll val,int l, int r, ndpf)
{
if (l <= ndl && ndr <= r)/*return es[o];*/
{
return (es[o].lbs.check(val));
}
if (r <= ndmid)return query(val,l, r, lsp);
else if (l >= ndmid + 1)return query(val,l, r, rsp);
else return (query(val,l, ndmid, lsp)||query(val,ndmid + 1, r, rsp));
}
}seg;
template<typename INint> inline void IN(INint &x)
{
x = 0; int f = 1; char c; cin.get(c);
while (c<'0' || c>'9') { if (c == '-')f = -1; cin.get(c); }
while (c >= '0'&&c <= '9') { x = x * 10 + c - '0'; cin.get(c); }
}
int main()
{
//freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//freopen("data.in", "r", stdin);
int T(1), cas(0);
while (cas, T--)
{
int n, q; cin >> n >> q;
//cout << clock()*1. / CLOCKS_PER_SEC << endl;
F(i, 1, n)
{
int sz; /*cin >> sz;*/IN(sz);
F(j, 1, sz)
{
ll v;
//cin >> v;
IN(v);
G[i].push_back(v);
}
}
//cout << clock()*1. / CLOCKS_PER_SEC << endl;
seg.build(1, 1, n);
//cout << clock()*1. / CLOCKS_PER_SEC << endl;
F(i, 1, q)
{
int le, ri; ll x;
//cin >> le >> ri >> x;
IN(le); IN(ri); IN(x);
node ret;
for (int k = 0; k < maxbit; ++k)ret.lbs.b[k] = (ll(1) << k);
int ans=seg.query(x,le, ri, 1, 1, n);
cout << (ans ? "NO" : "YES") << '\n';
}
//cout << clock()*1. / CLOCKS_PER_SEC << endl;
}
return 0;
}
题目:sequence
链接:https://ac.nowcoder.com/acm/contest/884/C
大意:给定两个长度为n(<=3e6)的序列a,b,求 max 1 ≤ l e ≤ r i ≤ n { ( min l e ≤ i ≤ r i a i ) ∗ ( ∑ l e ≤ j ≤ r i b j ) } \max \limits_{1\leq le \leq ri \leq n}\{(\min \limits_{le\leq i\leq ri }a_i )*(\sum \limits_{le\leq j\leq ri} b_j) \} 1≤le≤ri≤nmax{(le≤i≤riminai)∗(le≤j≤ri∑bj)}
分析:单调栈枚举作为最小值的a[i],获取le和ri的左边界和右边界,再在边界内选取最大或最小的le和ri使b的和最大或最小
代码
#include
#define F(i,a,b) for(int i=(a);i<=ll(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(3e6+8);
int a[maxn], b[maxn];
ll L[maxn], R[maxn];
int noL[maxn], noR[maxn];
//#define endl '\n'
template<typename INint> inline void IN(INint &x)
{
x = 0; int f = 1; char c; cin.get(c);
while (c<'0' || c>'9') { if (c == '-')f = -1; cin.get(c); }
while (c >= '0'&&c <= '9') { x = x * 10 + c - '0'; cin.get(c); }
x *= f;
}
int n;
void init(int n, int h[], int A[], int def)
{
stack<int>S; S.push(def);
F(i,1,n)
{
while (S.top() != def
&& h[S.top()]>=h[i])//TODO:大于(<=h[i])还是小于(>=h[i])
S.pop();
A[i] = S.top(); S.push(i);
}
}
void init2(int n, int h[], int A[], int def)
{
stack<int>S; S.push(def);
for(int i=n;i;--i)
{
while (S.top() != def
&& h[S.top()] >= h[i])//TODO:大于(<=h[i])还是小于(>=h[i])
S.pop();
A[i] = S.top(); S.push(i);
}
}
#define ls (o<<1)
#define rs (o<<1|1)
#define ndp o,ndl,ndr
#define ndmid ((ndl+ndr)>>1)
#define lsp ls,ndl,ndmid
#define rsp rs,(ndmid+1),ndr
#define ndpf ll o,ll ndl,ll ndr
struct node
{
ll mi, ma;
};
struct set_T
{
node es[maxn << 2];
node comb(const node&le, const node&ri)//TODO
{
return { min(le.mi,ri.mi),max(le.ma,ri.ma)};
}
void PushUp(ll o) { es[o] = comb(es[ls], es[rs]); }
void build(ll o, ll ndl, ll ndr,ll ar[])//TODO:自定义初始化数据
{
if (ndl == ndr) { es[o] = { ar[ndl],ar[ndl] }; return; }
build(lsp,ar); build(rsp,ar);
PushUp(o);
}
void prin(ndpf)//TODO:自定义打印信息
{
cout << o << ":[" << ndl << "," << ndr << "]:";
cout << es[o].mi<<' '<<es[o].ma << endl;
if (ndl == ndr)return;
prin(lsp); prin(rsp);
}
node query(ll l, ll r, ndpf)
{
if (l <= ndl && ndr <= r)return es[o];
if (r <= ndmid)return query(l, r, lsp);
else if (l >= ndmid + 1)return query(l, r, rsp);
else return comb(query(l, ndmid, lsp), query(ndmid + 1, r, rsp));
}
}seg;
namespace IO {
#define BUF_SIZE 100000
#define OUT_SIZE 100000
#define ll long long
bool IOerror = 0;
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if (p1 == pend) {
p1 = buf; pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if (pend == p1) { IOerror = 1; return -1; }
}
return *p1++;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline bool read(int &x) {
bool sign = 0; char ch = nc(); x = 0;
for (; blank(ch); ch = nc());
if (IOerror) return false;
if (ch == '-')sign = 1, ch = nc();
for (; ch >= '0'&&ch <= '9'; ch = nc())x = x * 10 + ch - '0';
if (sign)x = -x; return true;
}
inline bool read(ll &x) {
bool sign = 0; char ch = nc(); x = 0;
for (; blank(ch); ch = nc());
if (IOerror) return false;
if (ch == '-')sign = 1, ch = nc();
for (; ch >= '0'&&ch <= '9'; ch = nc())x = x * 10 + ch - '0';
if (sign)x = -x; return true;
}
inline bool read(double &x) {
bool sign = 0; char ch = nc(); x = 0;
for (; blank(ch); ch = nc());
if (IOerror) return false;
if (ch == '-')sign = 1, ch = nc();
for (; ch >= '0'&&ch <= '9'; ch = nc())x = x * 10 + ch - '0';
if (ch == '.') {
double tmp = 1; ch = nc();
for (; ch >= '0'&&ch <= '9'; ch = nc())tmp /= 10.0, x += tmp * (ch - '0');
}
if (sign)x = -x; return true;
}
inline bool read(char *s) {
char ch = nc();
for (; blank(ch); ch = nc());
if (IOerror) return false;
for (; !blank(ch) && !IOerror; ch = nc())*s++ = ch;
*s = 0; return true;
}
inline void read(char &c) {
for (c = nc(); blank(c); c = nc());
if (IOerror) { c = -1; return; }
}
//fwrite->write
struct Ostream_fwrite {
char *buf, *p1, *pend;
Ostream_fwrite() { buf = new char[BUF_SIZE]; p1 = buf; pend = buf + BUF_SIZE; }
void out(char ch) {
if (p1 == pend) {
fwrite(buf, 1, BUF_SIZE, stdout); p1 = buf;
}
*p1++ = ch;
}
void print(int x) {
static char s[15], *s1; s1 = s;
if (!x)*s1++ = '0'; if (x < 0)out('-'), x = -x;
while (x)*s1++ = x % 10 + '0', x /= 10;
while (s1-- != s)out(*s1);
}
void println(int x) {
static char s[15], *s1; s1 = s;
if (!x)*s1++ = '0'; if (x < 0)out('-'), x = -x;
while (x)*s1++ = x % 10 + '0', x /= 10;
while (s1-- != s)out(*s1); out('\n');
}
void print(ll x) {
static char s[25], *s1; s1 = s;
if (!x)*s1++ = '0'; if (x < 0)out('-'), x = -x;
while (x)*s1++ = x % 10 + '0', x /= 10;
while (s1-- != s)out(*s1);
}
void println(ll x) {
static char s[25], *s1; s1 = s;
if (!x)*s1++ = '0'; if (x < 0)out('-'), x = -x;
while (x)*s1++ = x % 10 + '0', x /= 10;
while (s1-- != s)out(*s1); out('\n');
}
void print(double x, int y) {
static ll mul[] = { 1,10,100,1000,10000,100000,1000000,10000000,100000000,
1000000000,10000000000LL,100000000000LL,1000000000000LL,10000000000000LL,
100000000000000LL,1000000000000000LL,10000000000000000LL,100000000000000000LL };
if (x < -1e-12)out('-'), x = -x; x *= mul[y];
ll x1 = (ll)floor(x); if (x - floor(x) >= 0.5)++x1;
ll x2 = x1 / mul[y], x3 = x1 - x2 * mul[y]; print(x2);
if (y > 0) { out('.'); for (size_t i = 1; i < y&&x3*mul[i] < mul[y]; out('0'), ++i) {}; print(x3); }
}
void println(double x, int y) { print(x, y); out('\n'); }
void print(char *s) { while (*s)out(*s++); }
void println(char *s) { while (*s)out(*s++); out('\n'); }
void flush() { if (p1 != buf) { fwrite(buf, 1, p1 - buf, stdout); p1 = buf; } }
~Ostream_fwrite() { flush(); }
}Ostream;
inline void print(int x) { Ostream.print(x); }
inline void println(int x) { Ostream.println(x); }
inline void print(char x) { Ostream.out(x); }
inline void println(char x) { Ostream.out(x); Ostream.out('\n'); }
inline void print(ll x) { Ostream.print(x); }
inline void println(ll x) { Ostream.println(x); }
inline void print(double x, int y) { Ostream.print(x, y); }
inline void println(double x, int y) { Ostream.println(x, y); }
inline void print(char *s) { Ostream.print(s); }
inline void println(char *s) { Ostream.println(s); }
inline void println() { Ostream.out('\n'); }
inline void flush() { Ostream.flush(); }
#undef ll
#undef OUT_SIZE
#undef BUF_SIZE
};
using namespace IO;
int main()
{
//freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T(1), cas(0);
while (cas, T--)
{
cin >> n;
//F(i, 1, n)IN(a[i]); F(i, 1, n)IN(b[i]);
F(i, 1, n)read(a[i]); F(i, 1, n)read(b[i]);
F(i, 1, n)L[i] = L[i - 1] + b[i];
//for (int i = n; i; --i)R[i] = R[i + 1] + b[i];
init(n, a, noL, 0);
init2(n, a, noR, n + 1);
seg.build(1, 0, n, L);
ll ans = -1ll * inf*inf;
F(i, 1, n)
{
ll val = a[i];
int le = noL[i] + 1, ri = noR[i] - 1;
ll tmp;
if (a[i] > 0)
{
//if (i == 3)
//cout << le << ' ' << ri << endl;
ll sumL = seg.query(le - 1, i - 1, 1, 0, n).mi;
sumL = L[i - 1] - sumL;
ll sumR = seg.query(i, ri, 1, 0, n).ma;
sumR = sumR - L[i];
ll sum = sumL + sumR + b[i];
tmp = val * sum;
ans = max(ans, tmp);
//cout << i << ' ' << sumL << ' ' << sumR << ' ' << tmp << endl;
//cout << endl;
}
else
{
ll sumL = seg.query(le - 1, i - 1, 1, 0, n).ma;
sumL = L[i - 1] - sumL;
ll sumR = seg.query(i, ri, 1, 0, n).mi;
sumR = sumR - L[i];
ll sum = sumL + sumR + b[i];
tmp = val * sum;
ans = max(ans, tmp);
//cout << i << ' ' << sumL << ' ' << sumR << ' ' << tmp << endl;
//cout << endl;
}
}
cout << ans << '\n';
}
return 0;
}
题目:triples I
链接:https://ac.nowcoder.com/acm/contest/884/D
大意:博士丢给你1e5个1e18范围内的数,让你对每个数n,给博士返回m个数,每个数都要是3的倍数,且这m个数的[或]要等于n
分析:[or]要等于n,那我们返回的数的二进制数中,'1’的bit肯定要是n中有的,并且n中的’1’的bit,我们要全部使用到。要控制每个数都是3的倍数,可以思考一下在二进制数中,怎样才能让一个数是3的倍数:对二进制中的i号位,如果i是偶数,那么它的权(也就是1<
代码
#include
#define F(i,a,b) for(int i=(a);i<=ll(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(20);
#define endl '\n'
ll val;
vector<int>pos[3];
ll makenum()
{
ll ret = 0;
F(i, 1, 2)
{
for (auto &x : pos[i])
{
ret |= (1ll << x);
}
}
return ret;
}
vector<ll>Ans;
void prin()
{
assert((Ans[0] | Ans[1]) == val);
assert((Ans[0] % 3 == 0));
assert(Ans[1] % 3 == 0);
cout << Ans.size();
for (auto &x : Ans)
{
cout << ' ' << x;
}
cout << '\n';
Ans.clear();
}
void solve()
{
if (val % 3 == 0)
{
cout << 1 << ' ' << val << '\n';
return;
}
pos[0].clear();
pos[1].clear();
pos[2].clear();
int p = 0;
ll tmp = val;
while (tmp)
{
int con;
if (p & 1)con = 2;
else con = 1;
if (tmp & 1)pos[con].push_back(p);
tmp >>= 1; ++p;
}
if (pos[1].size() && pos[2].size())
{
int yu = pos[1].size() + pos[2].size() * 2;
yu %= 3;
if (yu == 1)
{
ll sav = pos[1].back();
pos[1].pop_back();
ll v1 = makenum();
Ans.push_back(v1);
pos[1].clear();
while (pos[2].size() != 1)pos[2].pop_back();
pos[1].push_back(sav);
ll v2 = makenum();
Ans.push_back(v2);
prin();
}
else if (yu == 2)
{
ll sav = pos[2].back();
pos[2].pop_back();
ll v1 = makenum();
Ans.push_back(v1);
pos[2].clear();
while (pos[1].size() != 1)pos[1].pop_back();
pos[2].push_back(sav);
ll v2 = makenum();
Ans.push_back(v2);
prin();
}
}
else
{
vector<int>*it;
if (pos[1].size())
{
//assert(pos[1].size() > 3);
if(pos[1].size()<3)
return;
it = &(pos[1]);
}
else
{
//assert(pos[2].size() > 3);
if(pos[2].size()<3)
return;
it = &(pos[2]);
}
int yu = (*it).size() % 3;
vector<int>sav;
while ((*it).size() % 3 != 0)
{
sav.push_back((*it).back());
(*it).pop_back();
}
ll v1 = makenum();
Ans.push_back(v1);
for (auto&x : sav)
(*it).push_back(x);
reverse((*it).begin(), (*it).end());
while ((*it).size() != 3)(*it).pop_back();
ll v2 = makenum();
Ans.push_back(v2);
prin();
}
}
template<typename INint> inline void IN(INint &x)
{
x = 0; int f = 1; char c; cin.get(c);
while (c<'0' || c>'9') { if (c == '-')f = -1; cin.get(c); }
while (c >= '0'&&c <= '9') { x = x * 10 + c - '0'; cin.get(c); }
x *= f;
}
int main()
{
//#ifndef endl
//freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
//#endif // !endl
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//F(i, 1,1e6)val=i,solve();
//for (ll i = (ll)(1e18 - 1e6); i <= ll(1e18); ++i)val = i, solve();
int T(1), cas(0);
cin >> T;
while (cas, T--)
{
//cin >> val;
IN(val);
solve();
}
return 0;
}
题目:string
链接:https://ac.nowcoder.com/acm/contest/884/I
大意:在长为2e5的字符串S中,寻找最大的本质不同的子串集合,使对于结合中的任意两个子串a,b.a≠b且a≠rev(b),
分析:辣鸡字符串选手不会SAM::::构造字符串S2=S+(‘z’+1)+rev(S)
字符串S中的本质不同的子串s分为3类:
A: s是一个回文串,因此rev(s)是肯定等于s的
B. s不是一个回文串,但是S中有rev(s)
C.s不是回文串,且S中没有rev(s)
将这三类子串的数量暂且叫做nA,nB,nC
,那么我们要求的ans其实就使nA+nB/2+nC
我们对S2跑SAM(要处理一下,不处理超过边界即包含(‘z’+1)的串,其实将las赋值成1就可以了,也可以先直接跑S2,再减去包含(‘z’+1)的本质不同的子串的数量),得到的本质不同的子串的数量(不包含’z’+1)= nA + nB + nC*2 = cnt1
接下来我们对S跑一遍PAM,可以得到 S中回文串的数量= nA =cnt2
最后ans = (cnt1+cnt2)/2;
代码
//5_9-后缀自动机-求双串LCS和多串LCS
//本代码用来求多串LCS,可以使用SAM的LCS函数求双串LCS
#include
using namespace std;
#define F(i,a,b) for(int i=(a);i<=int(b);++i)
const int maxn = 4e5 + 9;
const int maxm = 28;
struct SAM
{
struct node
{
int len, fa;
int ch[maxm];
int pos;
string str;
}ns[maxn << 1];
int las, nid;
void init()
{
las = nid = 1;
ns[nid] = node();
}
void push_back(int c, int p)
{
assert(las > 0 && nid > 0);
int from = las; //p是之前的最长后缀类
int to = las = ++nid;//np是利用之前的最长后缀类生成的最长后缀类
ns[to] = node(); ns[to].len = ns[from].len + 1; ns[to].pos = p;
//ns[to].str = ns[from].str + char('a' + c); cout<
for (; from && !ns[from].ch[c]; from = ns[from].fa)ns[from].ch[c] = to;//更新las等价类及其后缀等价类的ch
int q = ns[from].ch[c];
if (!from)ns[to].fa = 1;//以上为case 1
else if (ns[q].len == ns[from].len + 1)ns[to].fa = q;//以上为case 2 找到的第一个las后缀+c的串就是from+c
else
{
ns[++nid] = ns[q]; ns[nid].len = ns[from].len + 1; ns[nid].pos = -1;
//ns[nid].str = ns[from].str + char('a' + c); cout << ns[nid].str << ' ' << ns[nid].len << endl;
ns[q].fa = ns[to].fa = nid;
for (; from&&ns[from].ch[c] == q; from = ns[from].fa)ns[from].ch[c] = nid;//以上为case 3
}
}
//此函数用来求双串LCS
int LCS(const int str[], int n)
{
int v = 1, ans = 0, len = 0;
F(i, 0, n - 1)
{
int c = str[i];
while (v&&ns[v].ch[c] == 0)v = ns[v].fa, len = ns[v].len;
if (v == 0)len = 0, v = 1;
else v = ns[v].ch[c], ++len;
ans = max(ans, len);
}
return ans;
}
//ans[maxn<<1]长度为,刚开始初始化为inf,之后再用LCS2跑每个字符串,tmp[1,maxn<<1]暂时保存str[]与自动机的最长子串
void LCS2(const int str[], int n, int ans[], int tmp[])
{
F(i, 0, nid)tmp[i] = 0;
int v = 1, len = 0;
for (int i = 0; i < n; ++i)
{
int c = str[i];
while (v&&ns[v].ch[c] == 0)v = ns[v].fa, len = ns[v].len;
if (v == 0)len = 0, v = 1;
else ++len, v = ns[v].ch[c];
tmp[v] = max(tmp[v], len);
}
F(i, 2, nid)tmp[i] = max(tmp[ns[i].fa], tmp[i]);
F(i, 2, nid)ans[i] = min(ans[i], tmp[i]);
}
//以fa指针为正向边,在li[1,nid]返回正向拓扑序
void get_topu(int li[], int in[])
{
F(i, 0, nid)in[i] = 0;
queue<int>Q; int id(0);
F(i, 2, nid)++in[ns[i].fa];
F(i, 2, nid)if (!in[i])Q.push(i);
while (Q.size())
{
int u = Q.front(); Q.pop();
li[++id] = u;
if (u == 1)break;
--in[ns[u].fa];
if (!in[ns[u].fa])
Q.push(ns[u].fa);
}
}
//以结点的ch指针为正向边,在li[1,nid]中打印正向拓扑序
void get_topu2(int u, int &id, int in[], int li[])
{
F(i, 1, nid)
F(j, 0, maxm - 1)if (ns[i].ch[j])
++in[ns[i].ch[j]];
queue<int>Q;
F(i, 1, nid)if (!in[i])Q.push(i);
while (Q.size())
{
int u = Q.front(); Q.pop();
li[++id] = u;
F(j, 0, maxm - 1)if (ns[u].ch[j])
if (--in[ns[u].ch[j]] == 0) Q.push(ns[u].ch[j]);
}
}
//使用前用get_topu获取拓扑序
//li[1,nid-2+1]为拓扑序列(fa树由底向上),inc暂时保存增量,cnt记录结点出现次数
void count(int li[], int inc[], int cnt[], const int str[], int n)
{
F(i, 0, nid)inc[i] = cnt[i] = 0;
for (int p = 1, i = 0; i < n; ++i)p = ns[p].ch[str[i]], ++inc[p];//初始化inc
F(i, 1, nid - 2 + 1)
{
int u = li[i];
cnt[u] += inc[u];
cnt[ns[u].fa] += cnt[u];
}
}
//使用前用get_topu2获取拓扑序
//li[1,nid]为拓扑序列(ch指针建为反向边),cnt[1,nid]统计每个自动机结点可识别的子串的个数
void count2(int li[], int inc[], int cnt[], const int str[], int n)
{
F(i, 2, nid)inc[i] = 1;
for (int i = nid; i; --i)
{
int u = li[i];
cnt[u] = inc[u];
for (int j = 0; j < maxm; ++j)if (ns[u].ch[j])
cnt[u] += cnt[ns[u].ch[j]];
}
}
//使用前用count2获取ch指针树子树结点数
string kth_substring(int cnt[], int k)
{
string ans;
int v = 1, sum(0);
while (sum != k)
{
int j = 0;
while (j < maxm&&sum + cnt[ns[v].ch[j]] < k)sum += cnt[ns[v].ch[j++]];
if (j == maxm) return "";
else ans.push_back(char('a' + j)), v = ns[v].ch[j], ++sum;
}
return ans + '\n';
}
//以fa指针反向为边,建立树(不建立指向父亲的边)
void create_fa_tree(vector<int>G[])
{
F(i, 1, nid)G[i].clear();
F(i, 2, nid)G[ns[i].fa].push_back(i);
}
//在fa反向边的树中dfs,由于上文没有建立指向fa的边,所以遍历不需要判father,这个dfs是在寻找str[]在自动机中的所有endpos(即为下函数服务)
void dfs_fa_tree(vector<int>G[], int u, vector<int>&endpos)
{
if (ns[u].pos != -1)endpos.push_back(ns[u].pos);
for (auto v : G[u])dfs_fa_tree(G, v, endpos);
}
//返回str[]在自动机中出现的endpos位置
int find(vector<int>G[], const int str[], int n, vector<int>&endpos)
{
create_fa_tree(G);//建立fa树
int v = 1;
F(i, 0, n - 1)
{
int to = ns[v].ch[str[i]];
if (to == 0)return -1;
else v = to;
}
//cout << ns[v].str << endl;
dfs_fa_tree(G, v, endpos);//dfs fa树,寻找位置
return 1;
}
}sam;
struct PAM {
struct nd
{
//长度,faile指针,最后更新该串的次数,出现次数,回文后缀个数(depth),
int len, fail, to[maxm];
//str可显示结点对应的字符串
string str;
int pos;
int inc, cnt, dep;
//vectorG;
} ns[maxn];
// s[1,size]为字符串,[0,nid]为结点空间,las为最后插入字符所对应的节点
int size, nid, las, s[maxn];
void init()
{
size = 0; nid = -1; las = 0;
ns[++nid] = nd(); ns[nid].len = 0; ns[nid].fail = nid + 1;//0结点 偶回文
ns[++nid] = nd(); ns[nid].len = -1; ns[nid].fail = nid - 1;//1结点 奇回文
s[0] = '$' + 1;
}
int getfail(int x) //沿fail找到第一个可插入的回文后缀
{
while (s[size] != s[size - ns[x].len - 1]) x = ns[x].fail;
return x;
}
void push_back(int c)
{
assert(ns[0].len == 0 && ns[1].len == -1);
s[++size] = c;
las = getfail(las); //找到插入的位置
if (!ns[las].to[c]) //若没有这个节点,则新建并求出它的fail指针
{
ns[++nid] = nd();
ns[nid].len = ns[las].len + 2;
ns[nid].fail = ns[getfail(ns[las].fail)].to[c];
ns[nid].dep = ns[las].dep + 1;
//ns[ns[nid].fail].G.push_back(nid);//维护字符串拓展树
ns[las].to[c] = nid;
//debug用,显示每个结点对应的字符串
//if (ns[nid].len == 1)ns[nid].str.push_back(char('a' + c));
//else ns[nid].str = char('a' + c) + ns[las].str + char('a' + c);
//cout << nid << ':' << ns[nid].str << '\n';
}
las = ns[las].to[c];
ns[las].inc++;
}
void count()//重新计算每个回文串出现的次数
{
for (int i = nid; i >= 0; --i)ns[i].cnt = 0;
for (int i = nid; i > 1; --i)
ns[i].cnt += ns[i].inc, ns[ns[i].fail].cnt += ns[i].cnt;
}
}pam;
string A, B;
int main()
{
//freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
int T(1);
//cin >> T;
while (T--)
{
sam.init(); pam.init();
cin >> A;
for (auto c : A)pam.push_back(c - 'a');
int cnt_huiwen = (pam.nid - 2) + 1;
B = A; reverse(B.begin(), B.end());
long long ans(0);
if (0)
{
A += char('z' + 1); A += B;
F(i, 0, A.size() - 1)sam.push_back(A[i] - 'a', i)/*, cout << i << ' ' << sam.ns[4].fa << endl*/;
F(i, 2, sam.nid)ans += sam.ns[i].len - sam.ns[sam.ns[i].fa].len/*, cout << ans << endl*/;
long long n = B.length();
ans -= (n + 1)*(n + 1);
}
if (1)
{
F(i, 0, A.size() - 1)sam.push_back(A[i] - 'a', i);
sam.las = 1;
F(i, 0, A.size() - 1)sam.push_back(B[i] - 'a', i + A.size());
F(i, 2, sam.nid)ans += sam.ns[i].len - sam.ns[sam.ns[i].fa].len/*, cout << ans << endl*/;
}
//cout << ans << '\n';
//cout << pam.nid - 2 + 1 << '\n';
ans += (pam.nid - 2) + 1;
assert(ans % 2 == 0);
ans /= 2;
cout << ans << '\n';
}
return 0;
}
//What to Debug
/*
-1.最好把全部warning都X掉,否则:https://vjudge.net/solution/19887176
0.看看自己是否有可能需要快读,禁endl
1.数组越界,爆int,浮点精度(查看精度是否达到题目要求,看有没有浮点数比较:eps),取模操作,初始化数组,边缘数据,输出格式(cas),强制在线是否更新了las
2.通读代码,代码无逻辑错误
3.读题,找到题意理解失误或算法错误
4.放弃
*/