C AB Substrings
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n;
int main()
{
scanf("%d",&n);
int ans=0,l=0,r=0,flag=false;
for(int i=1;i<=n;i++)
{
char s[15];
scanf("%s",s+1);
int m=strlen(s+1);
if(s[1]=='B') l++;
if(s[m]=='A') r++;
if(!(s[1]=='B'&&s[m]=='A')&&(s[1]=='B'||s[m]=='A')) flag=true;
for(int i=1;i<m;i++)
if(s[i]=='A'&&s[i+1]=='B') ans++;
}
ans+=min(l,r);
if(!flag&&min(l,r)>0) ans--;
printf("%d\n",ans);
}
D DivRem Number
考虑数论分块,这样的数在每个块里一定只有一个,并且它具有单调性,在每一块里面二分即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
long long n,ans=0;
scanf("%lld",&n);
long long l=1,r;
while(l<=n)
{
r=n/(n/l);
ll x=l,y=r;
while(x<=y)
{
ll m=x+y>>1;
if(n/m==n%m) {ans+=m;break;}
else if(n/m>n%m) y=m-1;
else x=m+1;
}
l=r+1;
}
printf("%lld\n",ans);
}
E XOR Partitioning
首先,符合答案的一定是某一个异或前缀和,如果这个前缀和是满足
s
u
m
x
=
z
{sum_x=z}
sumx=z
考虑一个划分,如果在位置 y {y} y可以分为两段,那么一定满足 s u m y = 0 {sum_y=0} sumy=0,因为 0 ⨁ z {0\bigoplus z} 0⨁z等于 z {z} z,说明 x + 1 {x+1} x+1到 y {y} y这一段的异或和为 z {z} z,处理前缀和之后,那么符合条件的序列一定是 z , 0 , z , 0 , z , 0... {z,0,z,0,z,0...} z,0,z,0,z,0...,把每一种情况的前缀和放入vector进行dp即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5,mod=1e9+7;
int n,a[N],sum[N];
vector<int>v[1300000];
int f(int l,int r){return sum[r]-sum[l-1];}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),a[i]^=a[i-1];
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+(a[i]==0);
v[a[i]].push_back(i);
}
ll ans=0;
for(int i=0;i<1<<20;i++)
{
if(!v[i].size()) continue;
if(a[n]!=0&&a[n]!=i) continue;
ll res=1,dp0=1,sumres=1;
for(int j=1;j<v[i].size();j++)
{
if(i==0) dp0=res+1,res=(res+dp0)%mod;
else
{
dp0=(dp0+sumres*f(v[i][j-1],v[i][j]))%mod;
res=dp0;
sumres=(sumres+res)%mod;
}
}
if(a[n]==0&&i==0) ans=(ans+dp0)%mod;
else if(a[n]==0) ans=(ans+sumres)%mod;
else ans=(ans+res)%mod;
}
printf("%lld\n",ans);
}
F Edge Ordering
考虑一条边如果不是最小生成树中的边,那么这条边一定比它与最小生成树所形成环上的最大的边还要大,否则我们可以砍掉环中一个比它大的边,把这条更小的边连上去。
那么假设我们现在按权值从大到小加边,那么在加入一条生成树中的边之前,就要先把与这条边形成环的非生成树中的边加进来。
设 d p s t {dp_{st}} dpst表示边的状态为 s t {st} st的树边的总和, s i s t {si_{st}} sist表示边的状态达到 s t {st} st的方案数,我们可以通过简单的dp做出来,但是由于这样需要考虑所有的边集,时间复杂度会达到 O ( 2 n n ) {O(2^{n^n})} O(2nn)。
我们考虑另外一种生成一个 1 , 2 , 3 , 4... {1,2,3,4...} 1,2,3,4...这样的升序排列的方法。
一开始排列为空,然后跑一下如图step1~5的步骤。
图中把每次加入的东西看作一条线,线选择一个位置后,从前往后延伸,最后各部分长度
叠加的部分就构成了一个排列。而发现这个线插入的位置可以决定第i各数的大小。
那么,考虑从大到小插入所有的边,如果是一条树边,我们只能插在最前面,如果是一条非树边,我们可以插在任意位置,step的虚线表示非树边可以插入的位置。
那么怎么维护权值呢,考虑当前状态为 n , b , c , s {n,b,c,s} n,b,c,s。 n {n} n表示加入的边数, b {b} b表示加入的树边的数量, c {c} c表示序列的种数, s {s} s表示所有 c {c} c种序列的树边的总和。
那么,如果加入一条树边 n , b , c , s − > n + 1 , b + 1 , c , s + c ∗ ( b + 1 ) {n,b,c,s->n+1,b+1,c,s+c*(b+1)} n,b,c,s−>n+1,b+1,c,s+c∗(b+1),因为这个树边只能加在最前面,所以种类数不变,而它的线的长度为 b + 1 {b+1} b+1。
如果加入非树边 n , b , c , s − > n + 1 , b , c ( n + 1 ) , s ( n + 2 ) {n,b,c,s->n+1,b,c(n+1),s(n+2)} n,b,c,s−>n+1,b,c(n+1),s(n+2)。即非树边插入n+1个空位中任意一个,所以种类数乘了 n + 1 {n+1} n+1,已经插入的线段的总和变成了 s ( n + 1 ) {s(n+1)} s(n+1),至于剩下的这 s {s} s,因为我们怎么加入非树边也只统计树边的总和,所以虽然加入的非树边长度的总和大于树边的长度,但由于我们只统计树边的长度,所以非树边的长度对原长度的影响就是原长度的总和。
如图所示,虚线为非树边,非树边线段的长度虽然大于树边,但是多出的长度不影响总和。
于是根据这个转移,如果我们连续加入a条非树边,那么
n
,
b
,
c
,
s
{n,b,c,s}
n,b,c,s移成了
n
+
k
,
b
,
(
n
+
1
)
⋅
⋅
⋅
(
n
+
k
)
c
,
(
n
+
2
)
⋅
⋅
⋅
(
n
+
k
+
1
)
s
{n + k, b,(n + 1)· · ·(n + k)c,(n + 2)· · ·(n + k + 1)s}
n+k,b,(n+1)⋅⋅⋅(n+k)c,(n+2)⋅⋅⋅(n+k+1)s。
因此假设加入树边i前要先加入
a
i
{a_i}
ai条非树边,我们可以直接
O
(
1
)
{O(1)}
O(1)转移。
那么回到之前设的 d p {dp} dp, d p s t {dp_{st}} dpst表示边的状态为 s t {st} st的树边的总和, s i s t {si_{st}} sist表示边的状态达到 s t {st} st的方案数,此时我们不用考虑非树边的状态了,我们只要预处理要加入的数量,时间复杂度变成 O ( n 2 n ) {O(n2^n)} O(n2n),足够解决这道问题,注意 b {b} b直接被状态表示出来了,每个状态的 n {n} n也可以预处理出来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=21,M=N*N,mod=1e9+7;
int n,m,up,sum[1<<20],e[1<<20],f[N],x[M],y[M],mp[N],bit[1<<20];
int Bit(int x){return x==0||bit[x]?bit[x]:bit[x]=__builtin_popcount(x);}
int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
ll p[M],invp[M],dp[1<<20],si[1<<20],len[1<<20];
ll Dp()
{
dp[up-1]=0;si[up-1]=1;
for(int i=up-1;i>=1;i--)
{
for(int j=1;j<=n;j++)
if(i>>j-1&1)
{
int st=i^(1<<j-1);
int n=len[i],a=sum[i]-sum[st];
len[st]=n+a+1;
ll c=si[i]*p[n+a]%mod*invp[n]%mod;
ll s=dp[i]*p[n+a+1]%mod*invp[n+1]%mod;
s=(s+c*(Bit((up-1)^st)));
(dp[st]+=s)%=mod;
(si[st]+=c)%=mod;
}
}
return dp[0];
}
int main()
{
p[0]=p[1]=invp[0]=invp[1]=1;
for(int i=2;i<M;i++) p[i]=p[i-1]*i%mod,invp[i]=(mod-mod/i)*invp[mod%i]%mod;
for(int i=2;i<M;i++) invp[i]=invp[i-1]*invp[i]%mod;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
mp[x[i]]|=1<<y[i]-1;
mp[y[i]]|=1<<x[i]-1;
}
up=1<<n;
for(int i=1;i<up;i++)
{
for(int j=1;j<=n;j++)
if(i>>j-1&1)
{
e[i]=e[i^(1<<j-1)]+Bit(i&mp[j]);
break;
}
}
for(int i=1;i<up;i++)
e[i]-=Bit(i)-1;
up=1<<n-1;
int st[N];
for(int i=1;i<up;i++)
{
for(int j=1;j<=n;j++) f[j]=j;
for(int j=1;j<n;j++)
if(i>>j-1&1)
f[getf(x[j])]=getf(y[j]);
memset(st,0,sizeof(st));
for(int j=1;j<=n;j++)
st[getf(j)]|=1<<j-1;
for(int j=1;j<=n;j++)
if(f[j]==j)
sum[i]+=e[st[j]];
}
printf("%lld\n",Dp());
}