HDU 多校 2023 Round #5 题解
HDU 多校 2023 Round #5 题解
A. Typhoon
题目大意
给一条
个点构成的折线, 次询问某个点到折线的最短距离。 数据范围:
。
思路分析
暴力枚举每一条线段,判断点到直线的垂足是否在线段上即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
#define double long double
using namespace std;
const int MAXN=1e4+5;
const double inf=1e18,eps=1e-6;
struct Point { ll x,y; } a[MAXN];
Point operator -(Point x,Point y){x.x-=y.x,x.y-=y.y;return x;}
ll operator *(Point x,Point y){return abs(x.x*y.y-x.y*y.x);}
inline ll dis(Point A,Point B){
return (A.x-B.x)*(A.x-B.x)+(B.y-A.y)*(B.y-A.y);
}
inline double dist(Point p,Point A,Point B) {
ll a=dis(p,A),b=dis(p,B),c=dis(A,B);
if(c&&a+c>=b&&b+c>=a) return (A-p)*(A-B)/sqrt(c);
else return sqrt(min(a,b));
}
signed main() {
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%lld%lld",&a[i].x,&a[i].y);
for(Point p;q--;) {
scanf("%lld%lld",&p.x,&p.y);
double ans=inf;
for(int i=1;i<n;++i) ans=min(ans,dist(p,a[i],a[i+1]));
printf("%.4Lf\n",ans);
}
return 0;
}
B. GCD Magic
题目大意
给定
,求下式的值: 数据访问:
。
思路分析
首先根据辗转相除法可以证明
若定义
其中
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5,MOD=998244353;
int n,m,k,B,mu[MAXN],C[15][15];
vector <int> primes,Mu;
bool isc[MAXN];
int idx1[MAXN],idx2[MAXN],val[MAXN];
int s0[MAXN],res[MAXN];
inline int ksm(int a,int b=MOD-2,int p=MOD) {
int ret=1;
while(b) ret=(b&1?1ll*ret*a%p:ret),a=1ll*a*a%p,b=b>>1;
return ret;
}
inline int idx(int k) { return k<=B?idx1[k]:idx2[n/k]; }
inline int calc(int n) {
int ans=k&1?(MOD-n):n;
for(int j=1,p=1;j<=k;++j) {
p=(p<<1)%MOD;
int tmp=1ll*(ksm(p,n)-1)*p%MOD*ksm(p-1)%MOD*C[k][j]%MOD;
if((k-j)&1) ans=(ans+MOD-tmp)%MOD;
else ans=(ans+tmp)%MOD;
}
return ans;
}
inline void solve() {
scanf("%d%d",&n,&k),m=0,B=sqrt(n);
for(int l=1,r;l<=n;l=r+1) {
r=n/(n/l),val[++m]=n/l;
if(val[m]<=B) idx1[val[m]]=m;
else idx2[n/val[m]]=m;
}
memset(s0,0,sizeof(s0));
for(int i=1,p=1;i<MAXN;++i) {
p=(p<<1)%MOD;
int f=ksm(p-1,k);
for(int j:Mu) {
if(i*j>=MAXN) break;
if(mu[j]==1) s0[i*j]=(s0[i*j]+f)%MOD;
else s0[i*j]=(s0[i*j]+MOD-f)%MOD;
}
}
for(int i=1;i<MAXN;++i) s0[i]=(s0[i]+s0[i-1])%MOD;
for(int i=m;i>=1;--i) {
int t=val[i];
if(t<MAXN) res[i]=s0[t];
else {
res[i]=calc(t);
for(int l=2,r;l<=t;l=r+1) {
r=t/(t/l);
res[i]=(res[i]+MOD-1ll*(r-l+1)*res[idx(t/l)]%MOD)%MOD;
}
}
}
int ans=0;
for(int l=1,r;l<=n;l=r+1) {
r=n/(n/l);
int sum=l==1?res[idx(r)]:(res[idx(r)]+MOD-res[idx(l-1)])%MOD;
ans=(ans+1ll*(n/l)*(n/l)%MOD*sum%MOD)%MOD;
}
printf("%d\n",ans);
}
signed main() {
mu[1]=1;
for(int i=2;i<MAXN;++i) {
if(!isc[i]) mu[i]=-1,primes.push_back(i);
for(int p:primes) {
if(i*p>=MAXN) break;
isc[i*p]=true,mu[i*p]=-mu[i];
if(i%p==0) { mu[i*p]=0; break; }
}
}
for(int i=1;i<MAXN;++i) if(mu[i]!=0) Mu.push_back(i);
for(int i=0;i<=10;++i) {
C[i][0]=1;
for(int j=1;j<=i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
}
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
C. String Magic (Easy Version)
题目大意
给定长度为
的字符串 ,求有多少 满足 且两个串均为回文。 数据访问:
。
思路分析
容易发现前一半与后一半相等的条件可以转化成
考虑枚举
稍作变换,我们要求的就是
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5;
char str[MAXN],ss[MAXN<<1];
int rd[MAXN<<1];
class SegmentTree {
private:
struct Node {
int ls,rs,cnt;
} tr[MAXN*30];
inline void pushup(int p) { tr[p].cnt=tr[tr[p].ls].cnt+tr[tr[p].rs].cnt; }
int siz;
public:
inline void Clear() { siz=0; }
inline void Insert(int u,int l,int r,int q,int &p) {
tr[p=++siz]={0,0,0};
if(l==r) return tr[p].cnt=tr[q].cnt+1,void();
int mid=(l+r)>>1;
if(u<=mid) tr[p].rs=tr[q].rs,Insert(u,l,mid,tr[q].ls,tr[p].ls);
else tr[p].ls=tr[q].ls,Insert(u,mid+1,r,tr[q].rs,tr[p].rs);
pushup(p);
}
inline int Query(int ul,int ur,int l,int r,int q,int p) {
if(ul<=l&&r<=ur) return tr[p].cnt-tr[q].cnt;
int mid=(l+r)>>1,res=0;
if(ul<=mid) res+=Query(ul,ur,l,mid,tr[q].ls,tr[p].ls);
if(mid<ur) res+=Query(ul,ur,mid+1,r,tr[q].rs,tr[p].rs);
return res;
}
} oddpal,evnpal;
int ot[MAXN],et[MAXN]; //odd and even
int oR[MAXN],eR[MAXN];
inline void solve() {
oddpal.Clear(),evnpal.Clear();
memset(rd,0,sizeof(rd));
memset(ot,0,sizeof(ot));
memset(et,0,sizeof(et));
scanf("%s",str+1);
int n=strlen(str+1),m=2*n+1;
for(int i=1;i<=m;++i) ss[i]=(i%2==0)?(str[i/2]):'#';
ss[0]='{',ss[2*n+2]='}';
for(int i=1,l=0,r=0;i<=m;++i) {
if(i<=r) rd[i]=min(r-i+1,rd[r+l-i]);
while(ss[i+rd[i]]==ss[i-rd[i]]) ++rd[i];
if(i+rd[i]-1>r) l=i-rd[i]+1,r=i+rd[i]-1;
}
for(int i=1;i<=n;++i) { //odd palin(i)
int rad=rd[2*i]/2;
oR[i]=i+rad-1;
oddpal.Insert(2*i-oR[i],0,n,ot[i-1],ot[i]);
}
for(int i=1;i<=n;++i) { //even palin(i-1,i)
int rad=(rd[2*i-1]-1)/2;
eR[i]=i+rad-1;
evnpal.Insert(2*i-eR[i]-1,0,n,et[i-1],et[i]);
}
ll ans=0;
for(int i=1;i<=n;++i) {
ans+=oddpal.Query(0,i,0,n,ot[i-1],ot[(i+eR[i])/2]);
if(eR[i]==i) continue;
ans+=evnpal.Query(0,i,0,n,et[i],et[(i+eR[i]+1)/2]);
}
printf("%lld\n",ans);
}
signed main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
D. String Magic (Hard Version)
题目大意
给定长度为
的字符串 ,对 的每个前缀都求出上一问的答案。 数据访问:
。
思路分析
考虑右端点在
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5;
char str[MAXN];
struct PAM {
struct Node {
int len,fail,son[26];
} tr[MAXN];
int lst,tot;
inline void init() {
tr[0].len=0, tr[0].fail=1,memset(tr[0].son,0,sizeof(tr[0].son));
tr[1].len=-1,tr[1].fail=0,memset(tr[1].son,0,sizeof(tr[1].son));
tot=1,lst=0,str[0]='#';
}
inline int node(int len) {
++tot,tr[tot].len=len,tr[tot].fail=0;
memset(tr[tot].son,0,sizeof(tr[tot].son));
return tot;
}
inline int add(int i) {
int c=str[i]-'a';
auto get=[&](int x) {
while(str[i]!=str[i-tr[x].len-1]) x=tr[x].fail;
return x;
};
int p=get(lst);
if(!tr[p].son[c]) {
int q=node(tr[p].len+2);
tr[q].fail=tr[get(tr[p].fail)].son[c];
tr[p].son[c]=q;
}
return lst=tr[p].son[c];
}
} P;
int sum[MAXN],fa[MAXN][20];
bool vis[MAXN];
inline void solve() {
memset(fa,0,sizeof(fa));
memset(vis,false,sizeof(vis));
memset(sum,0,sizeof(sum));
scanf("%s",str+1);
P.init();
int n=strlen(str+1);
ll ans=0;
for(int i=1;i<=n;++i) {
int p=P.add(i);
if(!vis[p]) {
auto fail=[&](int p) { return P.tr[p].fail; };
auto len=[&](int p) { return P.tr[p].len; };
sum[p]=sum[fail(p)],fa[p][0]=fail(p),vis[p]=true;
for(int k=1;k<20;++k) fa[p][k]=fa[fa[p][k-1]][k-1];
if(len(p)%2==0) {
int u=p;
for(int k=19;~k;--k) if(len(fa[u][k])>=len(p)/2) u=fa[u][k];
if(len(u)==len(p)/2) ++sum[p];
}
}
ans+=sum[p];
printf("%lld ",ans);
}
puts("");
}
signed main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
E. Snake
题目大意
将
排到 个队列里,每个队列长度在 之间,队列之间本质相同,但同一队列的元素先后顺序有影响,求方案数。 数据范围:
。
思路分析
考虑构造形式幂级数,从
然后考虑
那么原问题就转化成了求
直接二项式定理拆开,其中
预处理组合数后直接算就行,注意特判
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5,MOD=998244353;
inline int ksm(int a,int b=MOD-2,int p=MOD) {
int ret=1;
while(b) ret=(b&1?1ll*ret*a%p:ret),a=1ll*a*a%p,b=b>>1;
return ret;
}
int fac[MAXN],inv[MAXN];
inline int binom(int n,int m) {
if(m<0||m>n) return 0;
return 1ll*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline void solve() {
int n,m,k,ans=0;
scanf("%d%d%d",&n,&m,&k);
if(1ll*m*k<n) return puts("0"),void();
auto add=[&](int &x,int y) { x=(x+y>=MOD)?x+y-MOD:x+y; };
auto sub=[&](int &x,int y) { x=(x>=y)?x-y:x+MOD-y; };
for(int i=0;i<=m&&i*k<=n-m;++i) {
if(i&1) sub(ans,1ll*binom(m,i)*binom(n-i*k-1,m-1)%MOD);
else add(ans,1ll*binom(m,i)*binom(n-i*k-1,m-1)%MOD);
}
printf("%lld\n",1ll*ans*fac[n]%MOD*inv[m]%MOD);
}
signed main() {
fac[0]=1;
for(int i=1;i<MAXN;++i) fac[i]=1ll*fac[i-1]*i%MOD;
inv[MAXN-1]=ksm(fac[MAXN-1]);
for(int i=MAXN-1;i;--i) inv[i-1]=1ll*inv[i]*i%MOD;
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
F. Touhou Red Red Blue
题目大意
顺次给你
个元素,和两个槽 初始为空,每个元素有一个颜色在 之间,每当你拿到一个元素,你可以选择是否把这个元素保留,若保留:
为空放进 中。 - 否则若
为空放进 中。 - 否则:
- 若两个槽中和你手中的元素都同色,扔掉这三个元素,得
分,然后将 替换成一个任意的元素。 - 若两个槽中和你手中的元素两两异色,扔掉这三个元素,然后将
都替换成一个任意的元素。 - 否则扔掉
,把 放到 上,把你手中的元素放到 上。 求出最大可能的得分。
数据范围:
。
思路分析
直接 dp,状态里记录每个槽的状态然后模拟即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MP make_pair
mt19937 rnd(time(0));
const int MAXN=1e5+5;
int f[MAXN][4][4];
void upd(int &x,int y){x=max(x,y);}
void solve(){
string s;cin>>s;
int n=s.length();
memset(f,-0x3f,sizeof(f));
f[0][0][0]=0;
for(int i=0;i<n;i++)
for(int j:{0,1,2,3})
for(int k:{0,1,2,3}) if(j>0||j==0&&k==0){
int ch=(s[i]=='R'?1:(s[i]=='B'?2:3));
upd(f[i+1][j][k],f[i][j][k]);
if(j==k&&k==ch){
for(int t:{0,1,2,3}) upd(f[i+1][t][0],f[i][j][k]+1);
}
if(j&&k&&j!=k&&k!=ch&&ch!=j){
for(int t:{0,1,2,3})
for(int l:{0,1,2,3}) upd(f[i+1][t][l],f[i][j][k]);
}
if(j&&k){
upd(f[i+1][k][ch],f[i][j][k]);
}
if(!j) upd(f[i+1][ch][0],f[i][j][k]);
else if(!k) upd(f[i+1][j][ch],f[i][j][k]);
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j:{0,1,2,3}) for(int k:{0,1,2,3}) ans=max(ans,f[i][j][k]);
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
int _;cin>>_;
while(_--) solve();
}
G. Expectation (Easy Version)
题目大意
给一个初始为
的随机变量 ,有 轮操作,每轮有 的概率使 变成 。 给定
,求 的期望。 数据范围:
。
思路分析
设
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5,MOD=998244353;
inline int ksm(int a,int b=MOD-2,int p=MOD) {
int ret=1;
while(b) ret=(b&1?1ll*ret*a%p:ret),a=1ll*a*a%p,b=b>>1;
return ret;
}
int fac[MAXN],inv[MAXN],P[MAXN],Q[MAXN];
inline void solve() {
int n,m,a,b,ans=0;
scanf("%d%d%d%d",&n,&m,&a,&b);
int p=1ll*a*ksm(b)%MOD,q=(1+MOD-p)%MOD;
for(int i=P[0]=Q[0]=1;i<=n;++i) P[i]=1ll*P[i-1]*p%MOD,Q[i]=1ll*Q[i-1]*q%MOD;
for(int k=1,f=0;k<=n;++k) {
f=(f+ksm(k,m))%MOD;
ans=(ans+1ll*f*P[k]%MOD*Q[n-k]%MOD*fac[n]%MOD*inv[k]%MOD*inv[n-k]%MOD)%MOD;
}
printf("%d\n",ans);
}
signed main() {
for(int i=fac[0]=inv[0]=1;i<MAXN;++i) inv[i]=ksm(fac[i]=1ll*i*fac[i-1]%MOD);
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
H. Expectation (Hard Version)
题目大意
给一个初始为
的随机变量 ,有 轮操作,每轮有 的概率使 变成 。 给定
,求 的期望。 数据范围:
。
思路分析
依然写出上式,考虑用下降幂和第二类斯特林数表示
最后两行之间省略的过程见下:
另一个求和式也是同理可以化简,因此只需要求出斯特林数的第
一遍 NTT 即可求出,然后根据上式计算答案即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=(1<<21)+5,MOD=998244353,INV=(MOD+1)/3;
inline int ksm(int a,int b=MOD-2,int m=MOD) {
int ret=1;
while(b) {
if(b&1) ret=1ll*ret*a%m;
a=1ll*a*a%m;
b=b>>1;
}
return ret;
}
int rev[MAXN];
inline void NTT(int *f,int len,int op) {
for(int i=0;i<len;++i) if(i<rev[i]) swap(f[i],f[rev[i]]);
for(int s=2;s<=len;s=s<<1) {
int u=ksm(op==1?3:INV,(MOD-1)/s);
for(int i=0;i<len;i+=s) {
for(int j=i,w=1;j<i+s/2;++j) {
int x=f[j],y=1ll*w*f[j+s/2]%MOD;
f[j]=(x+y>=MOD)?x+y-MOD:x+y;
f[j+s/2]=(x>=y)?x-y:x+MOD-y;
w=1ll*w*u%MOD;
}
}
}
if(op==-1) {
int inv=ksm(len);
for(int i=0;i<len;++i) f[i]=1ll*f[i]*inv%MOD;
}
}
int fac[MAXN],inv[MAXN],binom[MAXN];
int F[MAXN],S[MAXN];
inline void solve() {
int n,m,a,b,p,ans=0;
scanf("%d%d%d%d",&n,&m,&a,&b),p=1ll*a*ksm(b)%MOD;
int lim=1<<(__lg(m*2)+1);
memset(S,0,sizeof(S)),memset(F,0,sizeof(F));
for(int i=0;i<=m;++i) S[i]=1ll*ksm(i,m)*inv[i]%MOD;
for(int i=0;i<=m;++i) F[i]=i&1?MOD-inv[i]:inv[i];
for(int i=0;i<lim;++i) {
rev[i]=(rev[i>>1]>>1);
if(i&1) rev[i]|=lim>>1;
}
NTT(S,lim,1),NTT(F,lim,1);
for(int i=0;i<lim;++i) S[i]=1ll*S[i]*F[i]%MOD;
NTT(S,lim,-1);
memset(binom,0,sizeof(binom)),binom[0]=1;
for(int i=1;i<=min(m+1,n);++i) binom[i]=1ll*binom[i-1]*(n-i+1)%MOD;
for(int i=0;i<=min(m+1,n);++i) binom[i]=1ll*binom[i]*inv[i]%MOD;
for(int k=0;k<=m;++k) {
int tmp=(1ll*binom[k]*ksm(p,k)%MOD+1ll*binom[k+1]*ksm(p,k+1)%MOD)%MOD;
ans=(ans+1ll*S[k]*fac[k]%MOD*tmp%MOD)%MOD;
}
printf("%d\n",ans);
}
signed main() {
fac[0]=1;
for(int i=1;i<MAXN;++i) fac[i]=1ll*fac[i-1]*i%MOD;
inv[MAXN-1]=ksm(fac[MAXN-1]);
for(int i=MAXN-1;i;--i) inv[i-1]=1ll*inv[i]*i%MOD;
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
I. Tree
题目大意
给定一棵
个点的树以及一种树链剖分方式,要求对每条链建线段树,树根接在链顶的父亲上,求新树的最大深度。 数据范围:
。
思路分析
注意到每个节点线段树上对应的深度是可以
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
inline char gc(){
static char buf[N],*p1=buf,*p2=buf;
if(p1==p2){
p2=(p1=buf)+fread(buf,1,N,stdin);
}
return *p1++;
}
inline int read(){
int x=0;char c=gc();
for(;c<'0'||c>'9';c=gc());
for(;c>='0'&&c<='9';c=gc())x=x*10+c-48;
return x;
}
int n;
int pre[N];
int hd[N],to[N],nxt[N],ec;
void ae(int u,int v){
++ec;
to[ec]=v;
nxt[ec]=hd[u];
hd[u]=ec;
}
int wson[N],dep[N];
int chsiz[N];
void dfs1(int u,int t){
chsiz[t]++;
for(int e=hd[u];e;e=nxt[e]){
int v=to[e];
dfs1(v,v==wson[u]?t:v);
}
}
void dfs2(int u,int t,int f){
if(u==t){
dep[u]=dep[f]+pre[chsiz[u]];
}else{
dep[u]=dep[t];
}
for(int e=hd[u];e;e=nxt[e]){
int v=to[e];
dfs2(v,v==wson[u]?t:v,u);
}
}
void solve(){
n=read();
ec=0;
memset(hd,0,sizeof(hd));
memset(chsiz,0,sizeof(chsiz));
int rt=0;
for(int i=1;i<=n;i++){
int f=read();
ae(f,i);
if(f==0){
rt=i;
}
}
for(int i=1;i<=n;i++){
wson[i]=read();
}
dep[rt]=0;
dfs1(rt,rt);
dfs2(rt,rt,rt);
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,dep[i]);
}
printf("%d\n",ans);
return;
}
int main(){
for(int i=1,j=0;i<N;i++){
while((1<<j)<i) j++;
pre[i]=j+1;
}
int ttt=read();
while(ttt--){
solve();
}
return 0;
}
J. Cut The Tree
题目大意
给一棵
个节点的树,节点带权 。 对于每个
,定义 表示 子树内最大的一对 , 表示 子树外最大的一对 。 求出最小的
。 数据范围:
。
思路分析
首先考虑
然后考虑
分别处理,最后计算答案即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5,inf=2e9;
typedef array<int,2> Pair;
struct Trie {
int ans,tot;
Pair vers;
struct Node { Pair ch; int idx; } tr[MAXN*32];
inline void init() {
for(int i=0;i<=tot;++i) tr[i]={{0,0},0};
ans=0,tot=0,vers={-1,-1};
}
inline void insert(int idx,int val) {
int res=0,p=0;
for(int k=31;~k;--k) {
int x=(val>>k)&1;
if(!tr[p].ch[x]) tr[p].ch[x]=++tot;
p=tr[p].ch[x];
}
tr[p].idx=idx;
p=0;
for(int k=31;~k;--k) {
int x=(val>>k)&1;
if(tr[p].ch[x^1]) res|=1ll<<k,p=tr[p].ch[x^1];
else p=tr[p].ch[x];
}
if(res>ans) ans=res,vers={idx,tr[p].idx};
}
} Trie;
int fa[MAXN],a[MAXN],out[MAXN],in[MAXN];
vector <int> G[MAXN];
int dfn[MAXN],rnk[MAXN],dcnt=0,siz[MAXN],hson[MAXN];
inline void dfs0(int u,int Fa) {
fa[u]=Fa,rnk[dfn[u]=++dcnt]=u,siz[u]=1;
for(int v:G[u]) if(v^Fa) {
dfs0(v,u),siz[u]+=siz[v];
if(siz[hson[u]]<siz[v]) hson[u]=v;
}
}
inline void dfs1(int u) {
for(int v:G[u]) if(v!=fa[u]&&v!=hson[u]) {
dfs1(v),Trie.init();
}
if(hson[u]) dfs1(hson[u]);
Trie.insert(u,a[u]);
for(int v:G[u]) if(v!=fa[u]&&v!=hson[u]) {
for(int x=dfn[v];x<dfn[v]+siz[v];++x) Trie.insert(rnk[x],a[rnk[x]]);
}
in[u]=Trie.ans;
}
inline void solve() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) G[i].clear(),hson[i]=0;
Trie.init();
for(int i=1;i<=n;++i) scanf("%d",&a[i]),Trie.insert(i,a[i]);
for(int i=1;i<=n;++i) out[i]=Trie.ans;
for(int i=1,u,v;i<n;++i) {
scanf("%d%d",&u,&v);
G[u].push_back(v),G[v].push_back(u);
}
dcnt=0,dfs0(1,0);
Pair vers=Trie.vers;
for(int t:{0,1}) {
int p=vers[t];
Trie.init();
vector <int> ci;
for(int u=p;u!=1;u=fa[u]) ci.push_back(u);
ci.push_back(1),reverse(ci.begin(),ci.end());
for(int i=0;i<(int)ci.size();++i) {
int u=ci[i];
if(u>1) out[u]=Trie.ans;
if(u==p) break;
Trie.insert(u,a[u]);
for(int v:G[u]) if(v!=ci[i+1]&&v!=fa[u]) {
for(int x=dfn[v];x<dfn[v]+siz[v];++x) Trie.insert(rnk[x],a[rnk[x]]);
}
}
}
out[1]=0,Trie.init();
dfs1(1);
int ans=inf;
for(int i=2;i<=n;++i) ans=min(ans,abs(in[i]-out[i]));
printf("%d\n",ans);
}
signed main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
K. Cactus Circuit
题目大意
给一个
个点的仙人掌,每条边可以持续运行 的时间,你要指定每条边开始运行的时间,使得任何时候这个仙人掌都是联通的。 数据范围:
。
思路分析
考虑每个环,显然只能是先断掉一条边,等有边无法运行时再加入,求出前三小的
对每个环暴力考虑,可以先建一棵生成树,对于非树边暴力跳 LCA 并统计信息即可,也可以用 tarjan 来缩点,答案最后记得和非环边取
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5,inf=2e9;
struct Edge { int u,v,w; };
int dsu[MAXN];
inline int find(int x) { while(x^dsu[x]) x=dsu[x]=dsu[dsu[x]]; return x; }
vector <Edge> G[MAXN];
int Fa[MAXN],dep[MAXN],wt[MAXN];
bool mark[MAXN];
inline void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
for(auto e:G[u]) if(e.v^fa) Fa[e.v]=u,wt[e.v]=e.w,dfs(e.v,u);
}
inline void solve() {
int n,m;
cin>>n>>m;
vector <Edge> cyc;
for(int i=1;i<=n;++i) G[i].clear(),Fa[i]=dep[i]=wt[i]=mark[i]=0,dsu[i]=i;
for(int i=1,u,v,w;i<=m;++i) {
cin>>u>>v>>w;
int x=find(u),y=find(v);
if(x==y) cyc.push_back({u,v,w});
else {
G[u].push_back({u,v,w});
G[v].push_back({v,u,w});
dsu[x]=y;
}
}
dfs(1,0);
int ans=inf;
for(auto e:cyc) {
array <int,3> val={e.w,inf,inf};
auto add=[&](int w) {
val[2]=min(val[2],max(w,val[1]));
val[1]=min(val[1],max(w,val[0]));
val[0]=min(val[0],w);
};
for(int u=e.u,v=e.v;u!=v;) {
if(dep[u]<dep[v]) swap(u,v);
assert(!mark[u]);
add(wt[u]),mark[u]=true,u=Fa[u];
}
ans=min({ans,val[0]+val[1],val[2]});
}
for(int i=2;i<=n;++i) if(!mark[i]) ans=min(ans,wt[i]);
cout<<ans<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int T; cin>>T;
while(T--) solve();
return 0;
}
L. Counting Stars
题目大意
给定
个点 条边的无向图,定义 表示图中有多少个 条边的菊花,求 。 数据范围:
。
思路分析
对于某个点
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5,MOD=1e9+7;
inline int ksm(int a,int b=MOD-2,int p=MOD) {
int ret=1;
while(b) ret=(b&1?1ll*ret*a%p:ret),a=1ll*a*a%p,b=b>>1;
return ret;
}
int ans[MAXN],deg[MAXN],fac[MAXN],inv[MAXN];
inline int binom(int n,int m) {
if(m<0||m>n) return 0;
return 1ll*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline void solve() {
int n,m;
cin>>n>>m;
memset(ans,0,sizeof(ans)),memset(deg,0,sizeof(deg));
for(int u,v;m--;) cin>>u>>v,++deg[u],++deg[v];
for(int i=1;i<=n;++i) {
auto add=[&](int &x,int y) { x=(x+y>=MOD)?x+y-MOD:x+y; };
for(int j=2;j<=deg[i];++j) add(ans[j],binom(deg[i],j));
}
int res=0;
for(int i=2;i<n;++i) res^=ans[i];
cout<<res<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
fac[0]=1;
for(int i=1;i<MAXN;++i) fac[i]=1ll*fac[i-1]*i%MOD;
inv[MAXN-1]=ksm(fac[MAXN-1]);
for(int i=MAXN-1;i;--i) inv[i-1]=1ll*inv[i]*i%MOD;
int T; cin>>T;
while(T--) solve();
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!