最后一次模拟赛了
不知为什么感觉有点失落
考场上已经想到正解的一半了,建圆方树DP
后面的步骤稍微思考了一下,发现要分治NTT+多项式多点求值(当场自闭)
于是就只有50分
正解:
考虑对每一个环计算答案,最后就是所有的环的答案乘起来
而每一个环的答案只与环长有关,容斥一下,发现答案是一个等比数列,直接求和做到O(logn)(快速幂复杂度)
一棵仙人掌的环长最多只有O(sqrt(n))种
所以总复杂度为O(Q*sqrt(n)*logn)
究级卡常题
代码:
#include
#include
#include
#include
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 200005
vector G[N],gid[N];
int fir[N],to[N],nxt[N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
}
int dfn[N],low[N],stk[N],sz[N],top,dc,tot;
void dfs(int u,int pp)
{
dfn[u]=low[u]=++dc;
stk[top++]=u;
for(int v,i=0;i<(int)G[u].size();i++){
if(gid[u][i]!=pp){
v=G[u][i];
if(!dfn[v]){
dfs(v,gid[u][i]);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
adde(u,++tot);sz[tot]=1;
while(top>0){
adde(tot,stk[--top]);
sz[tot]++;
if(stk[top]==v)break;
}
}
}
else low[u]=min(low[u],dfn[v]);
}
}
}
const int mod=998244353;
int C,n,m,Q,con[N],pw[35],lg[N];
inline int ksm(int x,int y)
{
if(y==1)return x;
if(y==2)return 1ll*x*x%mod;
if(y==3)return 1ll*x*x%mod*x%mod;
if(y==4)return 1ll*x*x%mod*x%mod*x%mod;
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
y>>=1;x=1ll*x*x%mod;
}
return ret;
}
inline int kksm(int y)
{
int ret=1;
for(;y;y-=(y&-y))
ret=1ll*ret*pw[lg[y&-y]]%mod;
return ret;
}
int main()
{
freopen("inkmaster.in","r",stdin);
freopen("inkmaster.out","w",stdout);
int i,u,v;
n=gi();m=gi();Q=gi();
lg[0]=-1;for(i=1;i<=n;i<<=1)lg[i]=lg[i>>1]+1;
for(i=1;i<=m;i++){
u=gi();v=gi();
G[u].push_back(v);gid[u].push_back(i);
G[v].push_back(u);gid[v].push_back(i);
}
tot=n;
for(i=1;i<=n;i++)
if(!dfn[i])dfs(i,0);
tot-=n;
for(i=1;i<=tot;i++)sz[i]=sz[i+n];
sort(sz+1,sz+tot+1);
int hcnt=0;
for(i=1;i<=tot;i++){
if(sz[i]!=sz[i-1]){
sz[++hcnt]=sz[i];
con[hcnt]=0;
}
con[hcnt]++;
}
int ans;
while(Q--){
C=gi();
ans=1;
int ni=ksm(C,mod-2),pp=1;
pw[0]=(1+mod-C)%mod;
for(i=1;(1<=mod)tmp-=mod;
ans=1ll*ans*ksm(tmp,con[i])%mod;
}
ans=1ll*ans*C%mod;
printf("%d\n",ans);
}
}
白送了70分没拿到啊啊啊。。。
显然,我们一定是先随机跳,跳到一定的范围之后直接走最优
我们可以二分这个范围的大小
然后就会发现一个概率DP中常见性质:所有在这个范围外的点的期望步数是相等的
设在这个范围外的期望是x
那么有方程:
x=1+(sumdis+(n-cnt)*x)/n
sumdis表示在mid范围内的点到终点的距离之和
cnt表示在mid范围内的点的个数
稍微移一下项得
x=(n+sumdis)/cnt
如果这个x大于了我们的mid,就需要增大mid,否则就减小
sumdis和cnt可以通过点分树维护
代码:(有点恶心)
#include
#include
#include
#include
#include
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
#define LOG 19
#define LL long long
const int INF=0x3f3f3f3f;
int n,Q;
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
namespace DFS{
vector T1[N],T2[N],sum1[N],sum2[N];
int dfa[N],dep[N];
int nrt,all,tmpsiz[N];bool vis[N];
void findrt(int u,int ff){
int mx=0;tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if((v=to[p])!=ff&&!vis[v]){
findrt(v,u);
tmpsiz[u]+=tmpsiz[v];
mx=max(mx,tmpsiz[v]);
}
}
mx=max(mx,all-tmpsiz[u]);
if(2*mx<=all)nrt=u;
}
int getrt(int u,int sz){
all=sz;nrt=-INF;
findrt(u,0);return nrt;
}
int mx1,mx2,tmp[N],tcnt,dis[N][LOG+2];
void pre(int u,int d,int ff){
tmp[++tcnt]=u;
mx1=max(mx1,dis[u][d]);
mx2=max(mx2,dis[u][d-1]);
tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
dis[v][d]=dis[u][d]+1;
pre(v,d,u);
tmpsiz[u]+=tmpsiz[v];
}
}
}
void DFZ(int u,int d){
dep[u]=d;vis[u]=1;
tcnt=mx1=mx2=0;pre(u,d,0);
T1[u].resize(mx1+1);sum1[u].resize(mx1+1);
T2[u].resize(mx2+1);sum2[u].resize(mx2+1);
for(int i=1;i<=tcnt;i++){
int x=tmp[i];
T1[u][dis[x][d]]++;
T2[u][dis[x][d-1]]++;
sum1[u][dis[x][d]]+=dis[x][d];
sum2[u][dis[x][d-1]]+=dis[x][d-1];
}
for(int i=1;i<=mx1;i++)T1[u][i]+=T1[u][i-1],sum1[u][i]+=sum1[u][i-1];
for(int i=1;i<=mx2;i++)T2[u][i]+=T2[u][i-1],sum2[u][i]+=sum2[u][i-1];
for(int v,p=fir[u];p;p=nxt[p])
if(!vis[v=to[p]]){
dfa[v=getrt(v,tmpsiz[v])]=u;
DFZ(v,d+1);
}
}
void work(){DFZ(getrt(1,n),1);}
pair query(int u,int k){
if(k<0)return make_pair(0,0);
LL ret=0,su=0;
LL c1=0,c2=0,s1=0,s2=0;
for(int t=u;t;t=dfa[t]){
if(k-dis[u][dep[t]]>=0){
c1=T1[t][min((int)T1[t].size()-1,k-dis[u][dep[t]])];
s1=sum1[t][min((int)sum1[t].size()-1,k-dis[u][dep[t]])];
}
else c1=s1=0;
ret+=c1-c2;
su+=(s1-s2)+(c1-c2)*dis[u][dep[t]];
if(dfa[t]&&k-dis[u][dep[t]-1]>=0){
c2=T2[t][min((int)T2[t].size()-1,k-dis[u][dep[t]-1])];
s2=sum2[t][min((int)sum2[t].size()-1,k-dis[u][dep[t]-1])];
}
else c2=s2=0;
}
return make_pair(ret,su);
}
}//----DFS----
int f[LOG][N],dep[N];
void dfs(int u)
{
dep[u]=dep[f[0][u]]+1;
for(int v,p=fir[u];p;p=nxt[p])
if((v=to[p])!=f[0][u])
f[0][v]=u,dfs(v);
}
int LCA(int x,int y)
{
if(dep[x]=0;i--)if((d>>i)&1)x=f[i][x];
if(x==y)return x;
for(int i=LOG-1;i>=0;i--)
if(f[i][x]!=f[i][y])
x=f[i][x],y=f[i][y];
return f[0][x];
}
int main()
{
freopen("branching.in","r",stdin);
freopen("branching.out","w",stdout);
int i,j,u,v,l,r,mid;
n=gi();Q=gi();
for(i=1;i>1;
pair tmp=DFS::query(v,mid);
if(1.0*(n+tmp.second)/tmp.first tmp=DFS::query(v,l-1);
double g=1.0*(n+tmp.second)/tmp.first;
printf("%.8f\n",min(g,1.0*dep[u]+dep[v]-2*dep[LCA(u,v)]));
}
}
题解:https://blog.csdn.net/qq_35950004/article/details/106836626