这是一次对我来说非常有技术含量的比赛。感谢教练能找到这套题。
一棵 n n n 个节点的树,有边权和点权。
d i s ( x , y ) dis(x,y) dis(x,y) 定义为 x x x 到 y y y 路径上的边权和。 v x v_x vx 为点 x x x 的点权。
无序点对 ( x , y ) (x,y) (x,y) 的权值定义为 d i s ( x , y ) ∗ ( v x xor v y ) dis(x,y) * (v_x \ \text{xor} \ v_y) dis(x,y)∗(vx xor vy)。
q q q 次操作,每次修改一个点的点权,然后询问当前所有不同无序点对的权值和。
n , q ≤ 30000 n,q \leq 30000 n,q≤30000,边权 [ 1 , 100 ] [1,100] [1,100],点权 [ 0 , 16384 ) [0,16384) [0,16384)。
两个长度为 n n n 的字符串 s 1 s1 s1, s 2 s2 s2。保证两者只可能由前 m m m 种小写英文字母组成。( a a a 是第一种, b b b 是第二种,……)并且两字符串 LCS 长度为 n − 1 n-1 n−1。
给定 s 1 s1 s1,问存在多少种不同的 s 2 s2 s2。
n ≤ 1 0 5 n \leq 10^5 n≤105, m ≤ 26 m \leq 26 m≤26。
一个 n n n 个点的无向完全图,每条边的边权都是 [ 1 , L ] [1,L] [1,L] 内的整数。
问有多少种不同的无向完全图满足 1 1 1 到 n n n 的最短路长为 k k k。
两个图不同当且仅当存在两图中各一条边,它们起点,终点相同但边权不同。
答案对 1 0 9 + 7 10^9 + 7 109+7 取模。
n , k ≤ 12 n,k \leq 12 n,k≤12, L ≤ 1 0 9 L \leq 10^9 L≤109。
70 分做法:
首先我们要考虑处理出修改前的所有不同无序点对的权值和。
这个东西有一个很自然很套路的分位考虑想法,按二进制位将原树拆成 15 个,每棵树的点权都是 0/1。
0/1 就有一个很 simple 的做法。考虑 dfs,对于每个点存储其为根的子树内 0 的个数,1 的个数,到所有 0 的距离和,到所有 1 的距离和。这样就能 O ( n ) O(n) O(n) 做了。
所以我们可以用 O ( 15 n ) O(15n) O(15n) 预处理。
对于修改,我们考虑只会影响目标点到根这一条链上的各种值,所以我们暴力把当前点在这一段的贡献去掉,然后再暴力重新加上。
总复杂度 O ( 15 n + 15 q ∗ d e p t h ) O(15n + 15q*depth) O(15n+15q∗depth)(depth 是树高)
在随机树下有很优秀的运行效率。
100 分做法:
我们考虑原来的预处理很像点分治。于是我们考虑真的用点分治做这道题。
这个的复杂度是 O ( 15 n log n + 15 q log n ) O(15n\log n + 15q \log n) O(15nlogn+15qlogn)
很有技巧的 DP。
首先考虑怎么判断两串的 LCS 是否长 n − 1 n-1 n−1。
正常的 LCSdp 策略是 L C S i , j LCS_{i,j} LCSi,j 表示一个串的前 i i i 个字符与另一个串的前 j j j 个字符的 LCS 长度,而这里我们很明显只需要 L C S i , i − 1 LCS_{i,i-1} LCSi,i−1, L C S i , i LCS_{i,i} LCSi,i, L C S i , i + 1 LCS_{i,i+1} LCSi,i+1。(下文用 L C S i , − 1 LCS_{i,-1} LCSi,−1, L C S i , 0 LCS_{i,0} LCSi,0, L C S i , 1 LCS_{i,1} LCSi,1 代替)
容易发现,若要求合法且可能满足最后 LCS 长度为 n − 1 n-1 n−1,那么肯定有:
i − 2 ≤ L C S i , − 1 ≤ i − 1 i-2 \leq LCS_{i,-1} \leq i-1 i−2≤LCSi,−1≤i−1
0 ≤ L C S i , 0 − L C S i , − 1 ≤ 1 0\leq LCS_{i,0} - LCS_{i,-1} \leq 1 0≤LCSi,0−LCSi,−1≤1
0 ≤ L C S i , 1 − L C S i , 0 ≤ 1 0\leq LCS_{i,1} - LCS_{i,0} \leq 1 0≤LCSi,1−LCSi,0≤1
可以发现,数组 { i − 2 , L C S i , − 1 , L C S i , 0 , L C S i , 1 } \{ i-2, LCS_{i,-1}, LCS_{i,0}, LCS_{i,1} \} {i−2,LCSi,−1,LCSi,0,LCSi,1} 的差分数组将会是一个 01 串。我们可以把这个 01 串作为实际 dp 的一维状态去进行转移。
现在我们定义 d p i , j , k dp_{i,j,k} dpi,j,k 表示:串 2 的前 i i i 个字符和串 1 组成的 LCSdp 差分数组状态为 j j j,且串 2 第 i i i 个字符为 k k k 时,串 2 的种类数。转移时可以直接把原数组还原,跑一遍 LCSdp 确定到达的状态,也可以预建出 LCSdp 状态转移的自动机。
由于我们把某个 dp 压在了另一个 dp 的状态之中,所以这种技巧常称为 dp 套 dp。
这里我采用的是转移时暴力 LCSdp,有一定的常数。合法的差分数组共有 5 个,转移常数在 3 左右。
所以时间复杂度是 O ( 15 n m ) O(15nm) O(15nm)。
很好的计数题。
考虑的步骤:想到用 dis 数组和最短路三角不等式 → \rightarrow → 想到容斥计算答案 → \rightarrow → 想到根据容斥原理把 dis 大于 k k k 的点压缩 → \rightarrow → 考虑到 dis 枚举困难,转为枚举 dis 数组对应的桶 → \rightarrow → 解决此题
直接上最后一步的结论。
我们令 b u c i buc_i buci 代表与 1 1 1 的最短路为 i i i 的点的个数。其中 0 ≤ i ≤ k + 1 0 \leq i \leq k + 1 0≤i≤k+1, i = k + 1 i = k+1 i=k+1 象征了所有 d i s > k dis > k dis>k 的情况。
dfs 枚举这个数组,方案数不会很多。
UPD:关于方案数的计算。其实就是把 n-2 个球塞到 k+1 个筐里,允许空筐的方案数。这个问题很基本,用插板法可以得出答案是 C n + k − 2 k C_{n+k-2}^{k} Cn+k−2k 。极限情况下这个数值是 6 ∗ 1 0 5 6 * 10^5 6∗105 左右。
已知一个确定的 b u c buc buc 数组,我们考虑如何计数。
假设现在枚举到下标 i i i。首先我们要把这个“个数”分配到点上,这个方案就是在剩余可选的点里选择 b u c i buc_i buci 个,是一个组合数。
然后,我们要让这些点满足 d i s = i dis = i dis=i。
对于所有 d i s < i dis < i dis<i 的点 x x x,我们要满足所有 d i s x + v ≥ i dis_x + v \geq i disx+v≥i 且存在 d i s x + v = i dis_x + v = i disx+v=i。这个东西用容斥统计一下即可。
然后对于所有 d i s = i dis = i dis=i 的其他点,与当前点的连边很明显可以从 1 1 1 取到 L L L。
最后我们解决 d i s > k dis > k dis>k 的问题。
容易发现,我们如果只要求对于所有 d i s ≤ k dis \leq k dis≤k 的点 x x x 满足 d i s x + v > k dis_x + v > k disx+v>k,并且对于所有 d i s > k dis > k dis>k 的其他点,与当前点的连边可以从 1 1 1 取到 L L L,那么这些点与其连出的边必然能导出一种 > k > k >k 的最短路。而且,如此统计不重不漏。
所以统计的方法相当于我们正常的容斥步骤去掉了“减去”那一步。
至此,我们就解决了确定 b u c buc buc 数组时的计数,答案是上述所有算出的方案相乘。
最后把所有情况相加即可。
总复杂度 O ( C n + k − 2 k × k 2 ) O(C_{n+k-2}^{k} \times k^2) O(Cn+k−2k×k2) 。
写了个动态点分治。
#include
#include
using namespace std;
const int N=3e4+1;
int bg[N],nx[N*2],to[N*2],va[N*2],tl;
inline void add(int x,int y,int v)
{
nx[++tl]=bg[x];
bg[x]=tl;
to[tl]=y;
va[tl]=v;
}
int tv[N],dep[N],val[N],fir[N],lg2[N*2],st[N*2][20],dfn;
inline int dist(int x,int y)
{
if(x==0||y==0) return 0;
if(fir[x]>fir[y]) swap(x,y);
int lg=lg2[fir[y]-fir[x]+1];
return dep[x]+dep[y]-min(st[fir[x]][lg],st[fir[y]-(1<<lg)+1][lg])*2;
}
void dfs(int now,int f,int depth)
{
fir[now]=++dfn;
st[dfn][0]=dep[now]=depth;
for(int i=bg[now];i;i=nx[i])
{
int aim=to[i];
if(aim==f) continue;
dfs(aim,now,depth+va[i]);
st[++dfn][0]=depth;
}
}
int book[N],siz[N],fa[N],sum[N][15][2],ctr;
long long ans[N][15],sumd[N][15][2],sumfd[N][15][2];
void dfs_prep(int now,int f)
{
siz[now]=1;
for(int i=bg[now];i;i=nx[i])
{
int aim=to[i];
if(aim==f||book[aim]) continue;
dfs_prep(aim,now);
siz[now]+=siz[aim];
}
}
void dfs_find(int now,int f,int out)
{
if(out>(siz[now]+out)/2) return;
int ok=1;
for(int i=bg[now];i;i=nx[i])
{
int aim=to[i];
if(aim==f||book[aim]) continue;
if(siz[aim]>(siz[now]+out)/2) ok=0;
dfs_find(aim,now,out+siz[now]-siz[aim]);
}
if(ok) ctr=now;
}
void build(int now)
{
book[now]=1;
for(int i=bg[now];i;i=nx[i])
{
int aim=to[i];
if(book[aim]) continue;
dfs_prep(aim,now),dfs_find(aim,now,0);
fa[ctr]=now,build(ctr);
}
}
int stk[N],top;
void modify(int pos,int v)
{
int i,j;
for(i=0;i<15;i++)
{
if((v&(1<<i))!=(val[pos]&(1<<i)))
{
for(j=pos;j;j=fa[j]) stk[++top]=j;
int tmp=stk[top--];
while(top)
{
int las=stk[top--];
ans[tmp][i]-=sumd[tmp][i][0]*2*sum[tmp][i][1]+sumd[tmp][i][1]*2*sum[tmp][i][0];
ans[tmp][i]+=2*sumfd[las][i][0]*sum[las][i][1]+2*sumfd[las][i][1]*sum[las][i][0]-ans[las][i];
tmp=las;
}
int tdis=dist(pos,fa[pos]),typ=val[pos]&(1<<i)?1:0;
ans[pos][i]+=2*sumd[pos][i][typ]-2*sumd[pos][i][typ^1];
sum[pos][i][typ^1]++,sum[pos][i][typ]--;
sumfd[pos][i][typ^1]+=tdis,sumfd[pos][i][typ]-=tdis;
tmp=pos;
for(j=fa[pos];j;j=fa[j])
{
sum[j][i][typ^1]++,sum[j][i][typ]--;
tdis=dist(pos,j);
sumd[j][i][typ^1]+=tdis,sumd[j][i][typ]-=tdis;
tdis=dist(pos,fa[j]);
sumfd[j][i][typ^1]+=tdis,sumfd[j][i][typ]-=tdis;
ans[j][i]+=sumd[j][i][0]*2*sum[j][i][1]+sumd[j][i][1]*2*sum[j][i][0];
ans[j][i]-=2*sumfd[tmp][i][0]*sum[tmp][i][1]+2*sumfd[tmp][i][1]*sum[tmp][i][0]-ans[tmp][i];
tmp=j;
}
}
}
val[pos]=v;
}
int main()
{
int n,q,i,j,root;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&tv[i]);
for(i=1;i<n;i++)
{
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
add(x,y,v),add(y,x,v);
}
dfs(1,0,0);
lg2[0]=-1;
for(i=1;i<=dfn;i++) lg2[i]=lg2[i>>1]+1;
for(i=1;i<20;i++)
for(j=1;j<=dfn-(1<<i)+1;j++)
st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
dfs_prep(1,0),dfs_find(1,0,0);
root=ctr,build(ctr);
for(i=1;i<=n;i++)
{
for(j=0;j<15;j++)
{
sum[i][j][0]++,sumfd[i][j][0]+=dist(i,fa[i]);
for(int k=fa[i];k;k=fa[k])
sum[k][j][0]++,sumd[k][j][0]+=dist(i,k),sumfd[k][j][0]+=dist(i,fa[k]);
}
}
for(i=1;i<=n;i++) modify(i,tv[i]);
scanf("%d",&q);
for(i=1;i<=q;i++)
{
int x,v;
scanf("%d%d",&x,&v);
modify(x,v);
long long tans=0;
for(j=0;j<15;j++) tans+=(1<<j)*ans[root][j];
printf("%lld\n",tans>>1);
}
return 0;
}
实际应用时,为防止 MLE 需要滚动数组。
#include
#include
using namespace std;
const int N=1e5+5;
long long dp[2][8][26];
char s[N];
int calc(int pos,int st,int tmp)
{
int dp1,dp2,dp3,ret1,ret2,ret3;
dp1=(st&4)?pos-1:pos-2;
dp2=(st&2)?dp1+1:dp1;
dp3=(st&1)?dp2+1:dp2;
ret1=max(dp1+(s[pos]==tmp+'a'),dp2);
ret2=max(dp2+(s[pos+1]==tmp+'a'),max(ret1,dp3));
ret3=max(dp3+(s[pos+2]==tmp+'a'),ret2);
return (ret1-pos+1)*4+(ret2-ret1)*2+(ret3-ret2);
}
int main()
{
int n,m,i,j,k;
scanf("%d%d%s",&n,&m,s+1);
if(n==1)
{
printf("%d",m-1);
return 0;
}
dp[1][6][s[1]-'a']=1;
if(s[1]!=s[2]) dp[1][5][s[2]-'a']=1;
for(i=0;i<m;i++) if(i!=s[1]-'a'&&i!=s[2]-'a') dp[1][4][i]=1;
for(i=1;i<n;i++)
{
int tmp=i&1;
for(j=2;j<=6;j++)
{
long long sum=0;
for(k=0;k<m;k++)
{
sum+=dp[tmp][j][k];
dp[tmp][j][k]=0;
}
for(k=0;k<m;k++)
{
int nxt=calc(i,j,k);
dp[tmp^1][nxt][k]+=sum;
}
}
}
long long ans=0;
for(i=2;i<=5;i++)
for(j=0;j<m;j++)
ans+=dp[n&1][i][j];
printf("%lld",ans);
return 0;
}
#include
#include
using namespace std;
const int N=15,MOD=1e9+7;
int n,k,l,disc[N],ans;
int C[N][N];
inline int qpow(int base,int expo)
{
int ret=1;
while(expo)
{
if(expo&1) ret=1ll*ret*base%MOD;
base=1ll*base*base%MOD;
expo>>=1;
}
return ret;
}
int solve()
{
int i,j,tmp=1;
for(i=1;i<=k;i++)
{
if(!disc[i]) continue;
int add=1,dec=1;
for(j=0;j<i;j++)
{
add=1ll*add*qpow(max(0,l-i+j+1),disc[j])%MOD;
dec=1ll*dec*qpow(max(0,l-i+j),disc[j])%MOD;
}
int fn=(0ll+add-dec+MOD)%MOD;
tmp=1ll*tmp*qpow(fn,disc[i])%MOD*qpow(l,disc[i]*(disc[i]-1)/2)%MOD;
}
for(i=0;i<=k;i++) tmp=1ll*tmp*qpow(max(0,l-k+i),disc[i]*disc[k+1])%MOD;
tmp=1ll*tmp*qpow(l,disc[k+1]*(disc[k+1]-1)/2)%MOD;
return tmp;
}
void dfs(int now,int rem,int cnt)
{
if(now==k+1)
{
disc[k+1]=rem;
int ctb=1ll*solve()*cnt%MOD;
ans=(0ll+ans+ctb)%MOD;
return;
}
for(int i=0;i<=rem;i++)
{
disc[now]+=i;
dfs(now+1,rem-i,1ll*cnt*C[rem][i]%MOD);
disc[now]-=i;
}
}
int main()
{
int i,j;
scanf("%d%d%d",&n,&k,&l);
C[0][0]=1;
for(i=1;i<=n;i++)
{
for(j=0;j<=i;j++)
{
if(j==0) C[i][j]=1;
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
}
disc[0]=1,disc[k]=1;
dfs(1,n-2,1);
printf("%d",ans);
return 0;
}