097
C - K-th Substring
题意:好像是查询子串第k小。
题解:后缀自动机,反正数据小,可能还有更简单的做法把。
代码:


# include <iostream> # include <cstring> # include <cstdio> using namespace std; const int N = 1e5 + 12; const int M = 2e5 + 12; char str[N]; int cur,cnt,last,nex[M][26],fa[M],dis[M],ch[M][26],l[M]; void build(int c,int id){ last = cur;cur = ++cnt; int u = last;dis[cur] = id; while(u &&!nex[u][c])nex[u][c] = cur,u = fa[u]; if(!u)fa[cur] = 1; else { int v = nex[u][c]; if(dis[v] == dis[u] + 1)fa[cur] = v; else { int nv = ++cnt;dis[nv] = dis[u] + 1; memcpy(nex[nv],nex[v],sizeof nex[v]); fa[nv] = fa[v];fa[v] = fa[cur] = nv; while(u && nex[u][c] == v)nex[u][c] = nv,u = fa[u]; } } } int len,n,Q,x,now,size; int bac[N],y[M],ans[N],f[M]; int main(){ scanf("%s",str); len = strlen(str); cnt = cur = 1; for(int i = 0;i < len;i++)build(str[i] - 'a',i + 1); for(int i = 1;i <= cnt;i++)bac[dis[i]]++; for(int i = 1;i <= len;i++)bac[i] += bac[i - 1]; for(int i = cnt;i >= 1;i--)y[bac[dis[i]]--] = i; for(int i = 1;i <= cnt;i++)f[i] = 1; for(int i = 1;i <= cnt;i++){ for(int j = 0;j < 26;j++){ if(nex[i][j])ch[i][++l[i]] = j; } } for(int i = cnt;i >= 1;i--){ for(int j = 1;j <= l[y[i]];j++){ f[y[i]] += f[nex[y[i]][ch[y[i]][j]]]; } } scanf("%d",&x); len = 0;now = 1;size = 0; while(x){ for(int i = 1;i <= l[now];i++){ if(f[nex[now][ch[now][i]]] < x)x -= f[nex[now][ch[now][i]]]; else { x--; str[size++] = 'a' + ch[now][i]; now = nex[now][ch[now][i]]; break; } } } str[size] = '\0'; puts(str); }
D - Equals
题意:有一个全排列,然后有m种交换方式,问经过数次交换能使在i的位置上的数为i的个数最大值
题解:并查集裸题
代码:


# include <iostream> # include <cstdio> using namespace std; const int N = 2e5 + 12; int fa[N],n,m,a[N],in[N],ans; int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);} int main(){ scanf("%d %d",&n,&m); for(int i = 1;i <= n;i++)scanf("%d",&a[i]),in[a[i]] = fa[i] = i; int x,y; for(int i = 1;i <= m;i++)scanf("%d %d",&x,&y),fa[find(y)] = find(x); for(int i = 1;i <= n;i++)if(find(in[i]) == find(i))ans++; printf("%d\n",ans); }
E - Sorted and Sorted
题意:有黑球白球全排列(1~n)混在了一起,变成了一个长度为2n的数组,问以交换相邻两数的方式给黑白球排序,只使黑白各自内部有序即可,问最少操作次数
题解:如果只有一个颜色就是逆序数了,两种也是这种贪心思想,但是要看在某个时间是先排白球优还是黑球优,套个dp即可
定义dp[i][j]表示已经使白球的1 - i和黑球的1 - j有序所需要的最少操作次数,
预处理出black[i][j] 表示在i位置前大于j的黑球有多少个,white[i][j]表示在i位置前大于j的白球有多少个
pos1表示编号为i白球所在位置,pos2表示编号为j黑球所在位置
那么 dp[i][j] = min(dp[i - 1][j] + white[pos1][i - 1] + black[pos1][j],dp[i][j - 1] + white[pos2][i] + black[pos2][j - 1])
最后输出dp[n][n]即可。
代码:


# include <iostream> # include <cstdio> using namespace std; const int N = 4e3 + 12; int a[N],b[N],black[N][N],white[N][N],n,bpos[N],wpos[N],dp[N][N]; int main() { scanf("%d",&n); for(int i = 1;i <= n << 1;i++) { char ch[2]; scanf("%s %d",ch,&a[i]);b[i] = ch[0]; if(b[i] == 'B')bpos[a[i]] = i; else wpos[a[i]] = i; } for(int i = 1;i <= n << 1;i++) for(int j = 0;j <= n;j++) { black[i][j] = black[i - 1][j] + (a[i - 1] > j && b[i - 1] == 'B'); white[i][j] = white[i - 1][j] + (a[i - 1] > j && b[i - 1] == 'W'); } for(int i = 0;i <= n;i++) for(int j = 0;j <= n;j++) { if(!i && !j)continue; int x = bpos[i],y = wpos[j]; dp[i][j] = 0x3f3f3f3f; if(i)dp[i][j] = min(dp[i][j],dp[i - 1][j] + black[x][i - 1] + white[x][j]); if(j)dp[i][j] = min(dp[i][j],dp[i][j - 1] + black[y][i] + white[y][j - 1]); } printf("%d\n",dp[n][n]); }
F - Monochrome Cat
题意:有只猫在树上任意起点,可以执行两种操作:
1,走一条边,并使终点颜色反转
2,反转当前点颜色
问使所有点颜色变成黑色的最小时间
题解:
首先把图简化下,黑色叶子结点是没用的,我们走到它一定不优,所以我们简化到图只剩白色叶子结点
考虑起点和终点相同的情况,无论在起点是任何点,每条边都会经过两次(经过三次一定不优)。那么每个点被第二种操作反转次数为它的度数
那么我们花费时间为2 * (点数 - 1) + (计算反转后为白色的点)
再考虑在这种情况下选择一条路径s-t,发现除了终点t,每个点经过次数都减少了1.(即路径s - t的边只经过一次)
那么我们的答案变成了2 * (点数 - 1) + (计算反转后为白色的点) - ((s - t路径上除t外计算反转后为白色的点)- (s - t路径上除t外上计算反转为黑色的点) + 链长)
令后面大括号为f[i]
从一个白色叶子节点为根,做树形dp,选出最优链即可。
f[i][0]表示以i的子树内(包括i)有一个起点s,且s 到 i这条链的最大f[i]
f[i][0]表示以i的子树内(包括i)有一个终点t,且i 到 t这条链的最大f[i]
dp即可。
代码:


# include <iostream> # include <cstdio> using namespace std; const int N = 2e5 + 12; const int inf = 0x3f3f3f3f; int n,head[N],dt,du[N],bac[N],que[N],rt,a[N],tot,p;char str[N];bool w[N]; struct Edge{int to,nex;}edge[N << 1]; void Add(int u,int v){edge[++dt] = (Edge){v,head[u]};head[u] = dt;du[u]++;} void Dfs(int u,int pre) { if(str[u] == 'W')w[u] = true; for(int i = head[u];i;i = edge[i].nex)if(edge[i].to != pre) { Dfs(edge[i].to,u); if(!w[edge[i].to])du[u]--,du[edge[i].to]--; w[u] |= w[edge[i].to]; } if(w[u])tot++; } void rebuild() { for(int i = 1;i <= n;i++)if(str[i] == 'W')rt = i; Dfs(rt,-1); for(int i = 1;i <= n;i++)if(w[i])a[i] = (str[i] == 'B') ^ (du[i] & 1),p += !a[i]; } int dp[N][2],f[N]; void dfs(int u,int pre) { f[u] = (tot - 1) * 2 + p; dp[u][0] = a[u] ? -1 : 1; for(int i = head[u];i;i = edge[i].nex)if(edge[i].to != pre && w[edge[i].to]) { dfs(edge[i].to,u); f[u] = min(f[u],f[edge[i].to]); f[u] = min(f[u],(tot - 1) * 2 + p - dp[u][0] - dp[edge[i].to][1] - 1); f[u] = min(f[u],(tot - 1) * 2 + p - dp[u][1] - dp[edge[i].to][0] - 1); dp[u][0] = max(dp[u][0],dp[edge[i].to][0] + 1 + (a[u] ? -1 : 1)); dp[u][1] = max(dp[u][1],dp[edge[i].to][1] + 1 + (a[u] ? -1 : 1)); } } int main() { scanf("%d",&n);int x,y; for(int i = 1;i < n;i++)scanf("%d %d",&x,&y),Add(x,y),Add(y,x); scanf("%s",str + 1);rebuild();dfs(rt,-1); printf("%d\n",!tot ? tot : f[rt]); }