CF1827 题解
CF1827题解
A
将
又因为前面
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5,MOD = 1e9 + 7;
int a[N],b[N],n,ans = 1;
int main()
{
int T;
cin>>T;
while(T--)
{
ans = 1;
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
for(int i = 1;i <= n;i++) cin>>b[i];
sort(a + 1,a + n + 1);
sort(b + 1,b + n + 1);
for(int i = 1,j = 1;i <= n - 1;i++)
{
while(b[j] < a[i] && j <= n) ++j;
--j;
ans = 1ll * ans * max((j - i + 1),0) % MOD;
}
if(a[n] <= b[n]) ans = 0;
cout<<ans<<endl;
}
return 0;
}
B(B2)
离谱陷阱题,个人认为难度高于C、D、E。
考虑每一个缝隙
枚举每个位置
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n,m;
struct BIT{
int a[N];
inline void init() {for(int i = 1;i <= m;i++) a[i] = n + 1;}
inline int lowbit(int x)
{
return x & (-x);
}
inline void md(int x,int k)
{
if(x > m) return;
a[x] = min(a[x],k);
md(x + lowbit(x),k);
}
inline int query(int x)
{
if(!x) return n + 1;
return min(query(x - lowbit(x)),a[x]);
}
}p;
int a[N],b[N],stk[N],pos[N],pre[N],suf[N],top = 0,pl[N];
inline int fd(int x)
{
int l = 1,r = m;
while(l < r)
{
int mid = (l + r) >> 1;
if(b[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
inline bool cmp(int x,int y)
{
return suf[x] > suf[y];
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i],b[i] = a[i];
sort(b + 1,b + n + 1);
m = unique(b + 1,b + n + 1) - (b + 1);
p.init();top = 0;
for(int i = 1;i <= n;i++) a[i] = fd(a[i]);
for(int i = 1;i <= n;i++)
{
while(top && stk[top] <= a[i]) --top;
pre[i] = (top ? pos[top] : 0);
stk[++top] = a[i],pos[top] = i;
}
top = 0;
for(int i = n;i >= 1;i--)
{
while(top && stk[top] <= a[i]) --top;
suf[i] = (top ? pos[top] : n + 1);
stk[++top] = a[i],pos[top] = i;
}
for(int i = 1;i <= n;i++) pl[i] = i;
sort(pl + 1,pl + n + 1,cmp);
long long ans = 0;
for(int i = 1,tp = n;i <= n;i++)
{
int x = pl[i];
while(tp >= suf[x]) p.md(a[tp],tp),--tp;
int f = p.query(a[x] - 1);
if(f > suf[x])
ans -= 1ll * (x - pre[x]) * (f - suf[x]);
}
for(int i = 2;i <= n;i++) ans += 1ll * (i - 1) * (n - i + 1);
cout<<ans<<endl;
}
return 0;
}
C
考虑好的字符串拆分成若干极小偶回文子串的方法是一定的,就是每次减去一个最短偶回文后缀,所以设
+1是表示最短回文后缀也是一个好的串。
现在考虑求
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n,m,p[N];
int f[N],g[N],nxt[N],pre[N];
char s[N],rd[N];
inline void read()
{
char k = getchar();
m = 0; s[m] = '?';
++m; s[m] = '#';
scanf("%s",rd + 1);
for(int i = 1;i <= n;i++)
{
++m;s[m] = rd[i];
++m;s[m] = '#';
}
s[m + 1] = ']';
}
inline void del(int x)
{
nxt[pre[x]] = nxt[x];
pre[nxt[x]] = pre[x];
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
read();
for(int i = 1;i <= n;i++) f[i] = 0;
for(int i = 1,r = 0,mid = 0;i <= m;i += 2)
{
if(r > i) p[i] = min(p[(mid << 1) - i],r - i + 1); else p[i] = 0;
while(s[i + p[i]] == s[i - p[i]]) ++p[i];
if(i + p[i] - 1 > r) r = i + p[i] - 1,mid = i;
}
for(int i = 1;i <= n;i++) pre[i] = i - 1,nxt[i] = i + 1;
for(int i = m;i > 0;i -= 2)
for(int j = (i + 1) >> 1;j <= (i + p[i] - 2) >> 1;j = nxt[j])
{
f[j] = (j << 1) - i + 1;
del(j);
}
long long ans = 0;
for(int i = 1;i <= n;i++)
if(f[i]) g[i] = 1 + g[i - f[i]],ans += g[i];
else g[i] = 0;
cout<<ans<<endl;
}
return 0;
}
D
考虑树的重心的两个性质:
1.增加一个节点,重心最多移动一条边。
2.要让树有两个重心,当且仅当树的节点数为偶数并且切开两个重心的边(两个重心相邻),可以将树切成等大的两部分。
对于本题,让树有两个重心的最快方法就是往原重心最大的子树中加点,使子树大小等于剩余部分大小,设子树大小为
所以我们考虑先建好树的结构,一个一个加点。维护每个点子树大小,重心,和重心最大的子树大小。
当加入一个点时:
-
如果这个点加在子树内,分为两种情况:
-
增加点所在的子树大小超过
,那么一定是 ,如果超过这个值,上一步就已经移动;并且此时 是奇数(若 是偶数上一步也进入这个情况,重心移动)。那么重心移动到相应儿子,新重心最大的子树是它的父亲(原重心方向),子树大小是 。 -
否则更新最大的子树大小。
-
-
如果加在子树外,则增加点所在的子树大小是重心的父亲往上的一部分大小,同上处理即可。
对于其中增加点在哪个子树的判断,
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
struct Edge{
int v,next;
}e[N * 2];
int n,dfn[N],tot = 0,head[N],fa[22][N],dep[N],siz[N];
struct BIT{
int a[N];
inline void init()
{
for(int i = 1;i <= n;i++) a[i] = 0;
}
inline int lowbit(int x)
{
return x & (-x);
}
inline void md(int x,int k)
{
for(;x <= n;x += lowbit(x)) a[x] += k;
}
inline int query(int x)
{
int ret = 0;
for(;x;x -= lowbit(x)) ret += a[x];
return ret;
}
}t;
inline void add(int x,int y)
{
++tot;
e[tot].v = y;
e[tot].next = head[x];
head[x] = tot;
}
inline void dfs(int x)
{
dfn[x] = ++tot; ++siz[x];
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].v;
fa[0][to] = x;
dep[to] = dep[x] + 1;
dfs(to);
siz[x] += siz[to];
}
}
inline int jump(int x,int d)
{
for(int i = 20;i >= 0;i--)
if(d & (1 << i)) x = fa[i][x];
return x;
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
t.init();
for(int i = 1;i <= n;i++) siz[i] = head[i] = dfn[i] = fa[0][i] = dep[i] = 0;
tot = 0;
for(int i = 2,x;i <= n;i++)
{
cin>>x;
add(x,i);
}
tot = 0;
dfs(1);
for(int i = 1;i <= 20;i++)
for(int j = 1;j <= n;j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
t.md(dfn[1],1);
for(int i = 2,g = 1,maxson = 0;i <= n;i++)
{
t.md(dfn[i],1);
if(dfn[i] <= dfn[g] + siz[g] - 1 && dfn[i] >= dfn[g])
{
int to = jump(i,dep[i] - dep[g] - 1);
int sizson = t.query(dfn[to] + siz[to] - 1) - t.query(dfn[to] - 1);
if(sizson > i / 2)
{
g = to;
maxson = i / 2;
}
else maxson = max(maxson,sizson);
}
else
{
int sizson = i - t.query(dfn[g] + siz[g] - 1) + t.query(dfn[g] - 1);
if(sizson > i / 2)
{
g = fa[0][g];
maxson = i / 2;
}
else maxson = max(maxson,sizson);
}
cout<<i - 2 * maxson<<" ";
}
cout<<endl;
}
return 0;
}
E
点分做法:G
这道题的确有点分做法,但博主调了四个小时,故先不管,讲个更简单的方法。
首先发现如果两个节点不能连通,那么它们下面的叶子节点也不能连通,所以只考虑叶节点即可。我们记录
对于一个叶节点,到达
但这并不是充要条件,我们找一个最深的
至于
时间复杂度
本题有些卡常,建议使用
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int eu[21][N * 3],head[N],dfn[N],rev[N * 3],n,m,tot = 0,anc[N],leaf[N],dep[N];
vector <int> G[N];
struct Edge{
int v,next;
}e[N * 2];
inline void add(int x,int y)
{
++tot;
e[tot].v = y;
e[tot].next = head[x];
head[x] = tot;
}
inline void dfs(int x,int last)
{
dfn[x] = ++tot; rev[tot] = x;
dep[x] = dep[last] + 1;
eu[0][tot] = dfn[x];
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].v;
if(to == last) continue;
dfs(to,x);
eu[0][++tot] = dfn[x];
}
if(!e[head[x]].next) leaf[x] = 1;
}
inline void dfs1(int x,int last)
{
if(leaf[x])
{
anc[x] = x;
for(auto v : G[x])
{
int s = min(dfn[x],dfn[v]),t = max(dfn[x],dfn[v]),d = log(t - s + 1) / log(2);
int lca = rev[min(eu[d][s],eu[d][t - (1 << d) + 1])];
if(dep[lca] < dep[anc[x]]) anc[x] = lca;
}
}
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].v;
if(to == last) continue;
dfs1(to,x);
}
}
inline void getanc(int rt)
{
tot = 0;
for(int i = 1;i <= n;i++) leaf[i] = 0;
dep[rt] = 0;
dfs(rt,0);
for(int i = 1;i <= 20;i++)
for(int j = 1;j + (1 << i) - 1 <= tot;j++)
eu[i][j] = min(eu[i - 1][j],eu[i - 1][j + (1 << (i - 1))]);
dfs1(rt,0);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
int x,y;
for(int i = 1;i <= n - 1;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
for(int i = 1;i <= m;i++) scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
getanc(1);
int now = 0;
for(int i = 1;i <= n;i++)
if(leaf[i])
if(!now || dep[anc[i]] > dep[anc[now]]) now = i;
getanc(anc[now]);
int rch = 1;
for(int i = 1;i <= n;i++)
{
if(!leaf[i]) continue;
if(anc[i] != anc[now])
{
printf("NO\n%d %d\n",now,i);
rch = 0;
break;
}
}
if(rch) printf("YES\n");
for(int i = 1;i <= n;i++) head[i] = dfn[i] = anc[i] = leaf[i] = dep[i] = 0,G[i].clear();
tot = 0;
}
return 0;
}
F
咕。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构