题目链接
https://ac.nowcoder.com/acm/contest/5666#question
题解
A题B-Suffix Array
题意:给你一个字符串s,同时定义B函数为: 其中,如果没有符合的j,那么。然后让你求出s字符串的所有后缀串按照B函数求出的字符串t排序后的顺序。
这个题有两种做法,一种是直接套用官方题解给出的结论直接求后缀数组就行,一种是按照正常思路来解,但是相对而言还算好想(
相比官方题解)。先讲第二种解法,也是比赛时还能想到的。
首先我们如果能想到一种直接能比较两个后缀大小的方法,那这个题就好办了,直接排序就行了。
下面讲一下怎样比较两个后缀。
考虑一个字符串s按照b函数求出来的新字符串t的形式,一定是开头一个0,然后是一堆数字之后紧跟着一个0,再一堆数字
类似于这种:,因此我们可以考虑先比较两个后缀的开头的连续相同字符个数,不妨定义一个数组dis[i]表示后缀从i开头的连续相同字符个数。
当两个后缀的dis数组不相同时,dis数组小的肯定在前面,eg:aaaab(01110)和aaaba(01102),这时aaaab肯定在aaaba前面。
当两个后缀的dis数组相同时,不妨假设一个后缀为aaabbbab(01101142),另一个后缀为bbbab(01102)。这种情况下我们可以考虑如果能够直接跨过前面0110的比较而直接比较1和2就方便了许多,假设第一个后缀为从i开始,第二个后缀为从j开始,可以得知[i,i+dis[i]]和[j,j+dis[j]]这个区间内的t字符数组都相等,即例子中的"011",而从i+dis[i]开始和j+dis[j]开始的t字符数组里,又有长度为lcp(i+dis[i],j+dis[j])的t字符数组也对应相等,因此我们就可以直接比较b[i+dis[i]+lcp]和b[j+dis[j]+lcp]了,值小的自然在前面。
当然还有两种特殊情况,当i+dis[i]>n时,eg:ba和a,这个时候ba直接放在前面就行了,而当j+dis[j]>n时,放在后面就行了(上一种情况的i和j调换过了的情况)。
时间复杂度:O(nlognlogn)
代码实现:
#include
using namespace std; const int N = 2e5+7; struct SA { int sa[N], ra[N], height[N]; int t1[N], t2[N], c[N]; void build(int *str, int n, int m) { str[n] = 0; n++; int i, j, p, *x = t1, *y = t2; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[i] = str[i]]++; for (i = 1; i < m; i++) c[i] += c[i - 1]; for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; for (j = 1; j <= n; j <<= 1) { p = 0; for (i = n - j; i < n; i++) y[p++] = i; for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[y[i]]]++; for (i = 1; i < m; i++) c[i] += c[i - 1]; for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for (i = 1; i < n; i++) x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) ? p - 1 : p++; if (p >= n) break; m = p; } n--; for (int i = 0; i <= n; i++) ra[sa[i]] = i; for (int i = 0, j = 0, k = 0; i <= n; i++) { if (k) k--; j = sa[ra[i] - 1]; while (str[i + k] == str[j + k]) k++; height[ra[i]] = k; } st_init(height, n); } int lg[N], table[23][N]; void st_init(int *arr, int n) { if (!lg[0]) { lg[0] = -1; for (int i = 1; i < N; i++) lg[i] = lg[i / 2] + 1; } for (int i = 1; i <= n; ++i) table[0][i] = arr[i]; for (int i = 1; i <= lg[n]; ++i) for (int j = 1; j <= n; ++j) if (j + (1 << i) - 1 <= n) table[i][j] = min(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]); } int lcp(int l, int r) { l = ra[l], r = ra[r]; if (l > r) swap(l, r); ++l; int t = lg[r - l + 1]; return min(table[t][l], table[t][r - (1 << t) + 1]); } }sa; string s; int n; int b[N],dis[N],res[N]; bool cmp(int i,int j){ if(dis[i]!=dis[j]) return dis[i] n) return true; if(j+dis[j]>n) return false; int lcp=sa.lcp(i+dis[i],j+dis[j]); // cout<>n>>s){ int pa=-1,pb=-1; for(int i=0;i =0;i--){ if(s[i]=='a'){ if(pb==-1) dis[i+1]=n-i; else dis[i+1]=pb-i; pa=i; } else{ if(pa==-1) dis[i+1]=n-i; else dis[i+1]=pa-i; pb=i; } } // for(int i=0;i 第二种做法(转自2020 牛客多校训练第一场之 A)
定义c[i]的值是i后面离“i指针”对应的字母最近的与s[i]同字符的距离,b数组就与这个c数组的后缀数组。
可以把这个c数组给模拟出来,求出它的后缀(当一个字母后面没有和他相同的字母时,无穷大了,就设为数组长度n,最后一位没啥用,随便设一个比较大的数。避免冲突,就设成n+1)。
根据关系求出sa数组,把这个字符串翻转过来,去掉第一位就能模拟出结果了。eg:设字符串为abaab,它的c数组为231556,列出c的后缀为: 231556,31556,1556,556,56,6。可以求得 sa:312456,反过来去掉首位的6,54213就是答案了。
代码实现:
#include
#include #include #include using namespace std; static const int N = 1e5+7; string s; int n; struct CharRank { int operator[](int i) const { return s[i]; } const int *s; }; template struct SA { void compute(int _n, const int *s) { n = _n; sort(n, IdOrder{}, CharRank{s}, sa); int range = rank(CharRank{s}, CharRank{s}); for (int length = 1; length < n && range < n; length <<= 1) { memcpy(trk, rk, sizeof(*rk) * n); TRank r1{n, length, trk}, r0{n, 0, trk}; sort(range + 1, IdOrder{}, r1, tsa); sort(range + 1, TOrder{tsa}, r0, sa); range = rank(r0, r1); } } int sa[N], rk[N]; private: struct IdOrder { int operator[](int i) const { return i; } }; struct TOrder { int operator[](int i) const { return sa[i]; } const int *sa; }; struct TRank { int operator[](int i) const { return i + shift < n ? rk[i + shift] + 1 : 0; } int n, shift; const int *rk; }; template void sort(int range, const Order &o, const Rank &r, int *out) { memset(count, 0, sizeof(*count) * range); for (int i = 0; i < n; ++i) { count[r[i]]++; } for (int i = 1; i < range; ++i) { count[i] += count[i - 1]; } for (int i = n; i--;) { out[--count[r[o[i]]]] = o[i]; } } template int rank(const Rank0 &r0, const Rank1 &r1) { rk[sa[0]] = 0; for (int _ = 1; _ < n; ++_) { int i = sa[_]; int j = sa[_ - 1]; rk[i] = rk[j] + (r0[i] != r0[j] || r1[i] != r1[j]); } return rk[sa[n - 1]] + 1; } int n; int count[N], tsa[N], trk[N]; }; int c[N + 1]; SA sa; int main() { while (~scanf("%d", &n)) { cin>>s; int pre1=n,pre2=n; for(int i=n-1;i>=0;i--){ if(s[i]=='a'){ if(pre1==n) c[i]=n-1; else c[i]=pre1-i-1; pre1=i; } else{ if(pre2==n) c[i]=n-1; else c[i]=pre2-i-1; pre2=i; } } c[n]=n; // for (int i = 0; i <= n; i++) // cout << c[i] << " "; // cout << endl; sa.compute(n+1,c); // for (int i = 0; i <= n; i++) // cout << sa.sa[i] << " "; // cout << endl; for (int i = n-1; i>=1 ; i--) { printf("%d ", sa.sa[i] + 1); } printf("%d\n", sa.sa[0] + 1); } return 0; } /* 2 aa 3 aba 5 abaab b 0 ab 00 aab 010 baab 0013 abaab 00213 0<00<0013<00213<010 aab ab abaab b baab 010 00 00213 0 0013 5 abaab 3 2 4 0 0 0 5 4 3 1 0 2 5 4 2 1 3 */ F题Infinite String Comparision
签到题,不多说,直接上代码
#include
using namespace std; int main() { ios::sync_with_stdio(0); string a,b; while(cin>>a>>b) { while(a.size()<1e5+10){ a+=a; } while(b.size()<1e5+10){ b+=b; } int i; for(i=0;i<1e5+10;i++){ if(a[i]>b[i]){ cout<<'>'< H题Minimum-cost Flow
题意:
给你n个点m条边的图,每条边都有一个单位代价w,以及容量,有q次询问,每次询问把每条边的容量都定为u/v,然后让你求出改变容量后跑流量为1时的最小费用。
题目给的q是最多为1e5,因此考虑每次重建图的做法肯定是不行的。
比较可行的做法是先按照容量为1跑出来最大流和最小费用,然后找出容量为1时和容量为u/v时的最小费用之间的关系。
首先已知一个定理:对于每条边容量相等的一条增广路径而言,费用是和容量成正比的。
因此我们可以先求出容量为1的最大流和费用,并且在存储下每条增广路上的费用。
这里我们设容量为1时跑出的最大流为MAX。
而当容量为u/v时,跑出的最大流为MAX*u/v,当MAX*u/v<1,即跑不出最大流为1的S到T的路径,输出NaN。
否则的话,我们把容量都设为u,那么求的就是最大流为v的费用。
因为我们之前已经预处理求出每条增广路径上的费用,并且存的时候已经保证是从小到大的了。
因此我们可以直接从前往后枚举费用,并把容量为1时的费用转换成容量为u时的费用,同时保证流的和不会超过v就行。
时间复杂度:O(E*q),因为增广路最多有100条。
代码实现:
#include
#include #include #include #include #include I题1 or 2
题意:给你一个无向图,让你判断能否选出一些边,使得每个点的度数为di(1<=di<=2)。
hdu3551的简单版本,详细题解请参考hdu3551。
代码实现:
#include
#include #include #include #include #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=1050; bool g[maxn][maxn],inque[maxn],inpath[maxn]; bool inhua[maxn]; int st,ed,newbase,ans,n; int base[maxn],pre[maxn],match[maxn]; int head,tail,que[maxn]; int x[maxn],y[maxn],f[maxn],mp[maxn][maxn],ne,np; void Push(int u) { que[tail]=u; tail++; inque[u]=1; } int Pop() { int res=que[head]; head++; return res; } int lca(int u,int v) { memset(inpath,0,sizeof(inpath)); while(1) { u=base[u]; inpath[u]=1; if(u==st) break; u=pre[match[u]]; } while(1) { v=base[v]; if(inpath[v]) break; v=pre[match[v]]; } return v; } void reset(int u) { int v; while(base[u]!=newbase) { v=match[u]; inhua[base[u]]=inhua[base[v]]=1; u=pre[v]; if(base[u]!=newbase) pre[u]=v; } } void contract(int u,int v)// { newbase=lca(u,v); memset(inhua,0,sizeof(inhua)); reset(u); reset(v); if(base[u]!=newbase) pre[u]=v; if(base[v]!=newbase) pre[v]=u; for(int i=1;i<=n;i++) { if(inhua[base[i]]){ base[i]=newbase; if(!inque[i]) Push(i); } } } void findaug() { memset(inque,0,sizeof(inque)); memset(pre,0,sizeof(pre)); for(int i=1;i<=n;i++) base[i]=i; head=tail=1; Push(st); ed=0; while(head 0)&&pre[match[v]]>0) contract(u,v); else if(pre[v]==0) { pre[v]=u; if(match[v]>0) Push(match[v]); else { ed=v; return ; } } } } } } void aug() { int u,v,w; u=ed; while(u>0) { v=pre[u]; w=match[v]; match[v]=u; match[u]=v; u=w; } } void edmonds() { memset(match,0,sizeof(match)); for(int u=1;u<=n;u++) { if(match[u]==0) { st=u; findaug(); if(ed>0) aug(); } } } void create() { n=0; memset(g,0,sizeof(g)); for(int i=1;i<=np;i++) for(int j=1;j<=f[i];j++) mp[i][j]=++n; for(int i=0;i J题Easy Integration
打表发现分子都是1,分母分别为6,30,140,630.....。oeis找规律发现分母的公式为。
官方题解给的是这是Wallis' integrals。详细证明参考https://en.wikipedia.org/wiki/Wallis%27_integrals。
代码实现:
#include
#define m_p make_pair #define p_i pair #define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i) #define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i) #define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n" #define mem(a, b) memset(a, b, sizeof(a)) #define mem0(a) memset(a, 0, sizeof(a)) #define fil(a, b) fill(a.begin(), a.end(), b); #define scl(x) scanf("%lld", &x) #define sc(x) scanf("%d", &x) #define abs(x) ((x) > 0 ? (x) : -(x)) #define PI acos(-1) #define lowbit(x) (x & (-x)) using namespace std; typedef long long LL; typedef unsigned long long ULL; const int maxn = 2000005; const int maxm = 1000005; const int maxp = 30; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; const double E = 2.718281828; inline int read() { int x(0), f(1); char ch(getchar()); while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } struct poi { }; int n; LL ji[maxn]; LL in[maxn]; map mp; inline LL quickPow(LL x, LL n, LL mod) { LL ans = 1; while(n) { if(n & 1) ans *= x, ans %= mod; x *= x, x %= mod; n >>= 1; } return ans; } // inline LL inv(LL x) { // return quickPow(x, mod - 2, mod); // } //x->a关于b的逆元 //y->b关于a的逆元 inline void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){ if (!b) {d = a, x = 1, y = 0;} else{ ex_gcd(b, a % b, y, x, d); y -= x * (a / b); } } inline LL inv(LL t){//如果不存在,返回-1 LL d, x, y; ex_gcd(t, mod, x, y, d); return d == 1 ? (x % mod + mod) % mod : -1; } inline LL C(int n, int m) { LL ans = ji[n]; if(ji[m] < maxn) ans *= in[ji[m]]; else ans *= inv(ji[m]); ans %= mod; if(ji[n - m] < maxn) ans *= in[ji[n - m]]; else ans *= inv(ji[n - m]); return ans % mod; // return ji[n] * inv(ji[m]) % mod * inv(ji[n - m]) % mod; } inline void sol() { if(mp.count(n)) { // printf("%lld\n", mp[n]); cout << mp[n] << "\n"; return; } LL ans = 0; _for(i, n + 1) { ans += C(n, i) * (mod + (i & 1 ? -1 : 1) * in[n + i + 1]) % mod; ans %= mod; // ans += mod + ji[n] * inv(ji[i]) % mod * inv(ji[n - i + 1]) % mod * ((i & 1) ? -1 : 1); ans %= mod; } cout << ans << "\n"; mp[n] = ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); ji[0] = 1; _rep(i, 1, maxn - 1) ji[i] = ji[i - 1] * i, ji[i] %= mod; _rep(i, 1, maxn - 1) in[i] = inv(i); while(cin>>n) { LL up=ji[2*n+1]%mod; LL down=ji[n]%mod*ji[n]%mod; LL ans=up*quickPow(down,mod-2,mod)%mod; cout<