8.25NOIP Day10模拟赛
T1
赛时一直在做容斥然后卡了一万年才过。
注意到每个字母是独立的,所以可以分开处理
发现可以选择一个点使得最大深度不超过2,那么我们可以维护从每一个点连续长度为1和2的链数量,然后枚举中心点跑出答案
#include<bits/stdc++.h>
#define mod 998244353
#define int long long
#define N 100005
using namespace std;
struct Ty{int u,v;}x[N];
vector<int>y[N];
int dpc,dpf,dis[N],node[N],summ=0;
signed main(){
int n,m;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)scanf("%lld%lld",&x[i].u,&x[i].v);
for(int i=1;i<=n;i++)node[i]=n-1;
for(int i=1;i<=m;i++){
node[x[i].u]--;
node[x[i].v]--;
y[x[i].u].push_back(x[i].v);
y[x[i].v].push_back(x[i].u);
}
for(int i=1;i<=n;i++)summ=(summ+node[i])%mod;
for(int i=1;i<=n;i++){
dis[i]=(summ-node[i]+mod)%mod;
for(int j=0;j<y[i].size();j++)dis[i]=(dis[i]-node[y[i][j]]+mod)%mod;
}
for(int i=1;i<=n;i++){
dpc=(dpc+dis[i]*node[i]%mod)%mod;
dpf=(dpf+dis[i]*node[i]%mod*node[i]%mod)%mod;
}
printf("%lld\n",dpc*dpc%mod*dpf%mod);
return 0;
}
T2
力大砖飞。
注意到消去的点数总和是 \(O(n)\) 级别的,同时每个节点的父节点不变,考虑使用并查集暴力维护合并
这里需要找LCA,有个trick是从u,v两个点交换着暴力往上跳打标记,碰到第一个打过标记的就是LCA,由于跳过的点都会被合并掉,所以不影响复杂度
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
vector<int>y[N];
int x[N],fa[N],vis[N];
int findd(int u){
if(x[u]==u)return u;
return x[u]=findd(x[u]);
}
void dfs(int u,int Fa){
fa[u]=Fa;
for(int i=0;i<y[u].size();i++)if(y[u][i]!=Fa)dfs(y[u][i],u);
return;
}
int update(int u,int v,int w){
int a=u,b=v,p=0;
while(1){
if(vis[a]==w&&a!=0){
p=a;
break;
}
vis[a]=w;
a=findd(fa[a]);
swap(a,b);
}
a=u;
b=v;
int now=0;
while(a!=p){
x[a]=w;
now++;
a=findd(fa[a]);
}
while(b!=p){
x[b]=w;
now++;
b=findd(fa[b]);
}
x[p]=w;
fa[w]=fa[p];
return now;
}
signed main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
y[u].push_back(v);
y[v].push_back(u);
}
for(int i=1;i<=n+m;i++)x[i]=i;
dfs(1,0);
int ans=n;
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
ans-=update(u,v,n+i);
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号