[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;
}
兴许青竹早凋,碧梧已僵,人事本难防。

浙公网安备 33010602011771号