Problem A magic
给出一个字符串$S$,和数字$n$,要求构造长度为$n$只含有小写字母的字符串$T$,
使得在$T$中存在删除且仅删除一个子串使得$S=T$成立。
输出$T$的构造方案数,mod 998244353的值。
对于$100 \% $的数据 $2 leq n leq 10^{18} , |S| leq 10^6$
Sol : 考虑$T$合法的条件是和$S$有相同的前缀和相同的后缀,且相同前后缀长度和是$|S|$
若最长公共前缀长度为$0$ ,那么说明$S$和$T$最后$|S|$位相同,合法情况$T$的取值有$26^{n - |S|}$ 种。
若最长公共前缀长度不为$0$ ,那么说明前半部分至少有$k$是$S$的前缀,后半部分就有$|S| - k $的长度是后缀,这个时候由于倒数$|S| - k$ 个 不能和上一次一样,这个位置只有$25$种可能,其他位置是$26$种可能,这种情况下方案数时$26^{n-|S|-1}$
最后答案就是$26^n - 26^{n-1} - |S| imes 25 imes 26^{n-|S|-1}$
注意需要特判$n = |S|$的情况,答案就是$26 ^ n - 1$
复杂度是$O(log_2 n)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const long long mod=998244353; long long n,m; char tmp[1000100]; long long Pow(long long x,long long k) { if(k<0) return 0ll; if(!k) return 1ll; long long res=Pow(x,k/2); res=res*res%mod; if(k%2) res=res*x%mod; return res; } int main() { scanf("%lld %s",&n,tmp); m=strlen(tmp); printf("%lld",(long long)(Pow(26ll,n)-(Pow(26ll,n-m)%mod+(m*25ll%mod)*Pow(26ll,n-m-1)%mod)%mod+mod)%mod); return 0; }
Problem B graph
给出可重边无自环不保证连通的无向图$G$ ,询问$u $到$v$简单路径上经过边权的最大值最小。
对于$100\% $ 的数据$n,m,q leq 3 imes 10^5$
Sol: 建出最小生成树(在建树过程考虑了重边了)。、
然后用并查集维护连通性。
最大边权最小等价于在最小生成树树上路径的最大值。
复杂度就是$O(m log_2 m)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# include<bits/stdc++.h> using namespace std; const int N = 3e5 + 10; map<int,int>mp[N]; int g[N][22],d[N][22],dep[N],fc[N],n,m,q; vector<pair<int , int> >E[N]; struct edge{ int u,v,w; }; vector<edge>Edge; bool cmp(edge a,edge b){return a.w < b.w;} inline int read() { int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X; } void write(int x) { if (x<0) x=-x,putchar('-'); if (x>9) write(x/10); putchar('0'+x%10); } void writeln(int x) { write(x); putchar(' '); } int father(int x) { if (fc[x]==x) return x; return fc[x]=father(fc[x]); } void kruskal() { sort(Edge.begin(),Edge.end(),cmp); for (int i = 1; i <= n; i++) fc[i] = i; for (int i = 0; i < Edge.size(); i++) { int u=Edge[i].u,v=Edge[i].v,w=Edge[i].w; int fx = father(Edge[i].u),fy = father(Edge[i].v); if (fx == fy) continue; fc[fx] = fy; E[u].push_back(make_pair(v,w)); E[v].push_back(make_pair(u,w)); } } void dfs(int u,int fa) { dep[u]=dep[fa]+1; g[u][0]=fa; for (int i=0;i<E[u].size();i++) { int v=E[u][i].first,w=E[u][i].second; if (v==fa) continue; d[v][0]=w; dfs(v,u); } } void init() { for (int i = 1 ; i<=n ; i++) if (!dep[i]) dfs(i,0); for (int i = 1; i <= 21 ; i++) for (int j = 1; j <= n ; j++) g[j][i] = g[g[j][i-1]][i-1], d[j][i] = max(d[j][i-1] , d[g[j][i-1]][i-1]); } int query(int u,int v) { int fx = father(u), fy = father(v); if (fx != fy) return -1; if (dep[u] < dep[v]) swap(u,v); int ret = 0; for (int i = 21 ; i >= 0 ; i--) if (dep[g[u][i]] >= dep[v]) ret = max(ret,d[u][i]),u=g[u][i]; if (u == v) return ret; for (int i = 21 ; i >= 0 ;i--) if (g[u][i] != g[v][i]) ret = max(max(ret , d[u][i]) , d[v][i]), u = g[u][i] , v = g[v][i]; return max(max(ret,d[u][0]),d[v][0]); } int main() { n=read();m=read();q=read(); for (int i=1;i<=m;i++) { int u=read(),v=read(),w=read(); if (mp[u].count(v) != 0) w = min(w , mp[u][v]); mp[u][v] = mp[v][u] = w; } map<int,int>::iterator it; for (int i = 1; i <= n ; ++i ) for (it = mp[i].begin() ; it != mp[i].end() ; ++it) Edge.push_back((edge){i , it->first , it->second}); kruskal(); init(); while (q--) { int u=read(),v=read(); writeln(query(u,v)); } return 0; }
Problem C number
定义不算前导零,只由两个数字构成的数为“好数”,如$101010$, $11111$
给出$T$个询问,询问$x$至少由几个好数相加组成的(可以同一个数用多次)。
对于$100\%$的数据$1 leq n leq 10^{18} ,1 leq T leq 100$
Sol:考虑$01$ , $02$ , $03$ , $04$最多四个数字就可以创造世界了,所以我们只需要判断$3$及以下的情况,剩余情况就输出$4$
我们可以枚举$C_{10}^{2} = 45$种选择数的方案$mask[i]$来构成这些数,然后用dp验证即可。
我们只至少这若干个数加起来是多少,我们要还原他为0,才能检验是否合法。
对于每个三个数累加的方案,我们都有这三个数中任意选2个任意选1个不选这7种子方案,也能获得相同的效果,我们特殊考虑,记录在$Sub[i]$中。
我们可以预处理出每个个选择数的方案,可以构成哪些最小合法的数(显然这些数都是小于等于$3 imes 9 =27$的)
设$f[i][j]$表示使用标号为$i$的方案,到达$j$数位上的信息,这个信息是一个3位二进制数,分别表示高位向低位退位为$i , iin[0,2]$是否合法。
转移的话可以用位运算转移。
最后如果当前数位为1,并且可以退0位的方案是ok的,那么我们就对对应取数的个数求min即可。(如果无法构成答案就是4了)
复杂度是$O(T imes C_{45}^{3} imes 18 imes 3 imes 3)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# include <bits/stdc++.h> # define int long long using namespace std; int mask[110],f[20010][22],s[55][55][55],val[20010],a[23]; bool ok[20010][34]; int size; vector<int>Sub[20010]; signed main() { for (int i = 0 ; i <= 9 ; i++ ) for (int j = i + 1 ; j <= 9 ; j++ ) mask[++mask[0]] = i * 10 + j; memset(ok, false, sizeof(ok)); int t = mask[0] ; mask[0] = 0; for (int i = 0 ;i <= t ; i++) for (int j = i ; j <= t ; j++) for (int k = j ; k <= t ;k++) { val[++size] = 3 - (i==0) - (j==0) - (k==0); ok[size][mask[i]/10 + mask[j]/10 + mask[k]/10] = ok[size][mask[i]%10 + mask[j]/10 + mask[k]/10] = true; ok[size][mask[i]/10 + mask[j]%10 + mask[k]/10] = ok[size][mask[i]/10 + mask[j]/10 + mask[k]%10] = true; ok[size][mask[i]%10 + mask[j]%10 + mask[k]/10] = ok[size][mask[i]%10 + mask[j]/10 + mask[k]%10] = true; ok[size][mask[i]/10 + mask[j]%10 + mask[k]%10] = ok[size][mask[i]%10 + mask[j]%10 + mask[k]%10] = true; s[i][j][k] = s[i][k][j] = s[j][i][k] = s[j][k][i] = s[k][i][j] = s[k][j][i] = size; if (s[0][j][k] != size) Sub[size].push_back(s[0][j][k]); if (s[i][0][k] != size) Sub[size].push_back(s[i][0][k]); if (s[i][j][0] != size) Sub[size].push_back(s[i][j][0]); if (s[0][0][k] != size) Sub[size].push_back(s[0][0][k]); if (s[0][j][0] != size) Sub[size].push_back(s[0][j][0]); if (s[i][0][0] != size) Sub[size].push_back(s[i][0][0]); if (s[0][0][0] != size) Sub[size].push_back(s[0][0][0]); } int T; scanf("%lld",&T); while (T--) { memset(f, 0 , sizeof(f)); int x; scanf("%lld",&x); a[0] = 0; while (x) { a[++a[0]] = x%10; x/=10;} for (int i = 1; i<= size; i++) f[i][a[0]+1] = 1; for (int i = a[0] ; i>=1 ; i--) { for (int j = 1; j <= size ; j++) { for (int last = 0 ; last <= 2 ; last++) if (f[j][i+1] & (1<<last)) for (int to = 0; to <= 2 ; to++) if (last*10 + a[i] - to >= 0 && ok[j][last*10 + a[i] - to]) { f[j][i] |= (1<<to) ; } for (int k = 0 ;k < Sub[j].size() ; k++) { int tmp = Sub[j][k] ; f[j][i] |= f[tmp][i]; } } } int ans = 4; for (int i = 1 ; i <= size ; i++) if (f[i][1] & 1) ans = min(ans , val[i]); printf("%lld ",ans); } return 0; }