[CSP-S模拟测试ex]题解
爆零了。少特判见祖宗。还好这场不计入总分。
考场上什么都没想。感觉考试状态又回到了两个月前。
A.Antipalindrome
手玩样例,不难发现题目中要求的合法串的充要条件是:对于任意$i \in [2,n]$,有$ s[i] \neq s[i-1]\ and \ s[i-1]\neq s[i+1]$
那么第一个位置有$m$种选择,第二个位置有$m-1$种,第三个位置往后都有$m-2$种。
$ans=m(m-1)(m-2)^{n-2}$
注意特判。如果判少会直接爆0。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const ll mod=1e9+7; int T; ll n,m; ll qpow(ll a,ll b) { ll res=1;a=a%mod; while(b) { if(b&1)res=res*a%mod; a=a*a%mod; b>>=1; } return res; } void work() { scanf("%lld%lld",&n,&m); m%=mod; if(m==1) { if(n==1)puts("1"); else puts("0"); return ; } if(n==1) { cout<<m<<endl; return ; } ll ans=m*(m-1)%mod; ans*=qpow(m-2,n-2),ans%=mod; cout<<ans<<endl; } int main() { scanf("%d",&T); while(T--)work(); return 0; }
B.Randomwalking
明显的换根dp。设$f[x]$表示x的子树对答案的贡献,$g[x]$表示x子树之外的部分对答案的贡献。那么$f[]$可以先用一遍dfs求得,然后在换根过程中求$g[]$顺便统计答案。
设$son[x]$为x的儿子数,显然这个要对根节点特判一下。那么可得$f[x]=a[x]+\sum \frac{f[y]}{son[x]}$,y是x的儿子。
然后考虑怎么换根。设fa为x的父亲,方程为$g[x]=\frac{g[fa]+(f[fa]-a[fa])*son[fa]-f[x]}{son[fa]}+a[fa]$。
解释一下,$(f[fa]-a[fa])*son[fa]$
相当与先还原出 $\sum f[s]$ (s为fa的儿子,包括x),
然后去掉$f[x]$的贡献,把剩下的作为x外部的点重新计算入$g[x]$。
最后统计答案的时候要比较的是$\frac{g[x]+(f[x]-a[x])*son[x]}{son[x]+1}+a[x]$,也相当与一个还原的过程。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } const int N=1e6+5; const double inf=9999999999999.0; typedef long long ll; int n,a[N]; int to[N<<1],head[N],nxt[N<<1],tot,deg[N],minn; double dp[N],g[N],minx=inf; void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; deg[x]++; } void dfs1(int x,int f) { dp[x]=a[x]; int d=f?deg[x]-1:deg[x]; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==f)continue; dfs1(y,x); dp[x]+=dp[y]/(1.0*d); } } void dfs2(int x,int f) { int df=(f==1?deg[f]:deg[f]-1); g[x]=(g[f]+(dp[f]-a[f])*df-dp[x])/df+a[f]; double now=(g[x]+(dp[x]-a[x])*(deg[x]-1))/deg[x]+a[x]; if(minx>now)minx=now,minn=x; for(int i=head[x];i;i=nxt[i]) if(to[i]!=f)dfs2(to[i],x); } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); add(x,y);add(y,x); } dfs1(1,0); minx=dp[1],minn=1; for(int i=head[1];i;i=nxt[i]) dfs2(to[i],1); cout<<minn<<endl; return 0; }
C.String
记录一下每个原来的位置被翻转到了哪里,用并查集维护一定相同的位置对应关系。如果一个联通块中有一个确定了,那么就都确定了。对于剩下的'?',用26进制的思想逐位填。
区间反转上Splay。卡常。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define re register using namespace std; const int L=1<<20|1; char buffer[L],*S,*T; #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) const int N=5e5+5,inf=0x3f3f3f3f; typedef long long ll; int n,m,a[N],b[N],tot,fa[N],vis[N],unsu[N],cnt; ll K; char str[N],s[N]; inline int rint() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } inline ll rll() { ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } inline void read(char x[]) { char ch=getchar();int now=0; while(ch!='?'&&(ch>'z'||ch<'a'))ch=getchar(); while(ch=='?'||(ch>='a'&&ch<='z'))x[++now]=ch,ch=getchar(); } int findf(int x) { if(fa[x]==x)return x; return fa[x]=findf(fa[x]); } namespace Splay { int fa[N],son[N][3],size[N],key[N],v[N],type,root; inline bool judge(int x){return son[fa[x]][1]==x;} inline void up(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;} inline void down(int x) { if(x&&v[x]) { v[son[x][0]]^=1;v[son[x][1]]^=1; swap(son[x][0],son[x][1]);v[x]=0; } } inline void rotate(int x) { int old=fa[x],oldf=fa[old],lr=judge(x); down(old);down(x); son[old][lr]=son[x][lr^1]; fa[son[old][lr]]=old; son[x][lr^1]=old;fa[old]=x; fa[x]=oldf; if(oldf)son[oldf][son[oldf][1]==old]=x; up(old);up(x); } inline void splay(int x,int goal) { for(int f;(f=fa[x])!=goal;rotate(x)) if(fa[f]!=goal) rotate(judge(x)==judge(f)?f:x); if(!goal)root=x; } int build(int f,int l,int r) { if(l>r)return 0; int mid=l+r>>1,x=++type; key[x]=a[mid];fa[x]=f; v[x]=0; son[x][0]=build(x,l,mid-1); son[x][1]=build(x,mid+1,r); up(x); return x; } inline int getrank(int x) { int now=root; while(1) { down(now); if(x<=size[son[now][0]])now=son[now][0]; else { x-=size[son[now][0]]+1; if(!x)return now; now=son[now][1]; } } } inline void rev(int l,int r) { l=getrank(l),r=getrank(r+2); splay(l,0);splay(r,l); down(root); v[son[son[root][1]][0]]^=1; } void print(int now) { down(now); if(son[now][0])print(son[now][0]); if(key[now]!=-inf&&key[now]!=inf)b[++tot]=key[now]; if(key[son[now][1]])print(son[now][1]); } } int main() { n=rint();m=rint();K=rll(); read(str); for(re int i=1;i<=n;i++) fa[i]=a[i+1]=i; a[1]=-inf;a[n+2]=inf; Splay::root=Splay::build(0,1,n+2); for(re int i=1;i<=m;i++) { int l=rint(),r=rint(); Splay::rev(l,r); } Splay::print(Splay::root); /*for(int i=1;i<=n;i++) cout<<b[i]<<' '; puts(" ");*/ for(re int i=1;i<=n;i++) { int fx=findf(b[i]),fy=findf(i); if(fx==fy)continue; if(str[fy]=='?')str[fy]=str[fx]; fa[fx]=fy; } for(re int i=1;i<=n;i++) { int fx=findf(i); s[i]=str[fx]; if(s[i]=='?'&&!vis[fx])vis[fx]=1,unsu[++cnt]=fx; } K--;int now=cnt; while(K) { str[unsu[now]]='a'+K%26; now--; K/=26; } while(now>0)str[unsu[now]]='a',now--; for(re int i=1;i<=n;i++) { if(s[i]=='?')s[i]=str[findf(i)]; putchar(s[i]); } putchar('\n'); return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。