C.Concatenation
题意:给定n个字符串,求一个将他们拼接起来的方案,使得结果的字典序最小。
分析:对n个字符串排序,a在b前面的条件是ab<ba,为什么这样正反拼接是正确的呢?在S1为S2子串的情况下,我们要考虑把谁放在前面,那就比一比谁放前面更小就可以了。
代码:
不加&会超时!不加&会超时!不加&会超时!传引用传引用传引用!!!
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+6;
int T,n,a[N];
string s[N];
int cmp(string &a, string &b)
{
string s1 = a + b, s2 = b + a;
return s1 < s2;
}
int main()
{
cin>>n;
for( int i=1; i<=n; ++i ) cin>>s[i];
sort(s + 1, s + n + 1, cmp);
for(int i = 1; i <= n; i++) cout << s[i];
return 0;
}
A.Ancestor
题意:给出两棵编号为1~n的树A和B,A、B树上每个节点均有一个权值,给出k个关键点的编号x1...xn,问有多少种方案使得去掉恰好一个关键点使得剩余关键点在树A上LCA的权值大于树B上LCA的权值。
分析:
需要用到一个结论——树上多个点的LCA为这多个点中DFS序最大的点和DFS序最小的点的LCA。故可以先求出所给k个关键点的DFS序,然后枚举删去哪一个。记得枚举前预处理LCA,要不然会超时。预处理其实只用求3+3次就可以了,分别为:删掉A的DFS最小(Ahead),删掉A的DFS序最大(Atail),删掉A的DFS序中间元素。
枚举每个点的时候 if-else 判断一下该点在A、B树中的地位即可。
代码:
(有点长,主要是因为A、B两棵树形态不同,所以需要分开求。)
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int MAXN=105000;
int N,K,x,ans=0;
int d1,d2;
int w1[MAXN],w2[MAXN]; //存两棵树每个点的weight
int key[MAXN];//题目中提到的K个key number
int ds1[MAXN],ds2[MAXN];// 存dfs序 dfs sort
int Log2[MAXN],faA[MAXN][30],faB[MAXN][30],depA[MAXN]={0},depB[MAXN]={0};//找LCA需要的一些数组
bool visA[MAXN]={false},visB[MAXN]={false};//找LCA需要的一些数组
vector< vector<int> > treeA,treeB;//存树
vector< pair<int,int> > KDS1,KDS2;//第一个数存顶点的dfs序,第二个数存顶点编号
void TOinput()
{
for(int i=1;i<=K;i++) cin>>key[i];
for(int i=1;i<=N;i++) cin>>w1[i];
for(int i=2;i<=N;i++)
{
cin>>x;
treeA[x].push_back(i);
treeA[i].push_back(x);
}
for(int i=1;i<=N;i++) cin>>w2[i];
for(int i=2;i<=N;i++)
{
cin>>x;
treeB[x].push_back(i);
treeB[i].push_back(x);
}
}
void do_pre()
{
Log2[1] = 0;
for (int i = 2; i <= N; ++i)
Log2[i] = Log2[i / 2] + 1;
}
void dfsA(int cur, int fath)
{
if (visA[cur]) return;
visA[cur] = true;
depA[cur] = depA[fath] + 1;
faA[cur][0] = fath;
ds1[cur]=d1++;
for (int i = 1; i <= Log2[depA[cur]]; i++) faA[cur][i] = faA[faA[cur][i - 1]][i - 1];
for (int i=0; i<treeA[cur].size();i++)
dfsA(treeA[cur][i], cur);
}
void dfsB(int cur, int fath)
{
if (visB[cur]) return;
visB[cur] = true;
depB[cur] = depB[fath] + 1;
faB[cur][0] = fath;
ds2[cur]=d2++;
for (int i = 1; i <= Log2[depB[cur]]; i++) faB[cur][i] = faB[faB[cur][i - 1]][i - 1];
for (int i=0; i<treeB[cur].size();i++)
dfsB(treeB[cur][i], cur);
}
int lcaAA(int a, int b)
{
if (depA[a] > depA[b]) swap(a, b);
while (depA[a] != depA[b]) b = faA[b][Log2[depA[b] - depA[a]]];
if (a == b) return a;
for (int k = Log2[depA[a]]; k >= 0; k--)
{
if (faA[a][k] != faA[b][k]) a = faA[a][k], b = faA[b][k];
}
return faA[a][0];
}
int lcaBB(int a, int b)
{
if (depB[a] > depB[b]) swap(a, b);
while (depB[a] != depB[b]) b = faB[b][Log2[depB[b] - depB[a]]];
if (a == b) return a;
for (int k = Log2[depB[a]]; k >= 0; k--)
{
if (faB[a][k] != faB[b][k]) a = faB[a][k], b = faB[b][k];
}
return faB[a][0];
}
void TOdelete()
{
sort(KDS1.begin(),KDS1.end());
sort(KDS2.begin(),KDS2.end());
int Ahead=KDS1[0].second,Atail=KDS1[K-1].second,Bhead=KDS2[0].second,Btail=KDS2[K-1].second;
int A1,A2,A3,B1,B2,B3;
A1=lcaAA(KDS1[1].second,Atail);//头被删了
A2=lcaAA(Ahead,KDS1[K-2].second);//尾巴被删了
A3=lcaAA(Ahead,Atail);//删的是中间的
B1=lcaBB(KDS2[1].second,Btail);
B2=lcaBB(Bhead,KDS2[K-2].second);
B3=lcaBB(Bhead,Btail);
for(int i=1;i<=K;i++)
{
int lcaA,lcaB;
if(key[i]==Ahead) lcaA=A1;
if(key[i]==Atail) lcaA=A2;
if(key[i]!=Ahead && key[i]!=Atail) lcaA=A3;
if(key[i]==Bhead) lcaB=B1;
if(key[i]==Btail) lcaB=B2;
if(key[i]!=Bhead && key[i]!=Btail) lcaB=B3;
if(w1[lcaA]>w2[lcaB]) ans++;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>N>>K;
treeA.resize(N+10);
treeB.resize(N+10);
TOinput();
do_pre();
d1=1; d2=1;
dfsA(1,0); for(int i=1;i<=K;i++) KDS1.push_back({ds1[key[i]],key[i]}); //把通过dfs得到的字典序存进去
dfsB(1,0); for(int i=1;i<=K;i++) KDS2.push_back({ds2[key[i]],key[i]});
TOdelete();//枚举 删去哪一个key number点
cout<<ans;
return 0;
}