bot 的能量堆(energy)
假设当前数为 n n n,两个数之差为 d d d。考虑除以一个质数 p p p后, ( n , d ) → ( ⌊ n p ⌋ , d p ) , ( ⌈ n p ⌉ , d p ) (n,d)\to (\lfloor\frac{n}{p}\rfloor,\frac{d}{p}),(\lceil\frac{n}{p}\rceil,\frac{d}{p}) (n,d)→(⌊pn⌋,pd),(⌈pn⌉,pd),也就是说有向上和向下取整两种情况。注意到两种情况只会往加减次数少的那一边走,可以近似认为只有一种情况。
根据整除分块的基本知识我们知道合法的状态数只有 d ( n ) d(n) d(n)级别(假设每次都是向下取整),因此直接记忆化搜索即可。
n = m n=m n=m的情况很傻逼。也就只能暴搜了。复杂度 O ( 玄学 ) O(玄学) O(玄学)。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
int T,n,m;
int pr[105],cnt,now;
map<pair<int,int>,int>id;
void dfs(int m,int n,int stp){
if(stp>=now||id.find(make_pair(m,n))!=id.end()&&stp>=id[make_pair(m,n)])return;now=min(now,stp+n-1);
id[make_pair(m,n)]=stp;
for(int i=1;i<=cnt;i++){
if(m%pr[i]==0){
int k=n%pr[i];
if(n<pr[i]){
now=min(now,stp+pr[i]-n+1);
}
else {
if(k<=pr[i]-k){
dfs(m/pr[i],(n-k)/pr[i],stp+k+1);
}
if(pr[i]-k<=k){
dfs(m/pr[i],(n-k)/pr[i]+1,stp+pr[i]-k+1);
}
}
}
}
}
int main(){
freopen("energy.in","r",stdin);
freopen("energy.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
cin>>n>>m;
if(n==m){
cout<<1<<"\n";
}
else {
if(n>m)swap(n,m);m-=n,id.clear();
cnt=0;int m2=m;
for(int i=2;i<=m2/i;i++){
if(m2%i==0){
pr[++cnt]=i;while(m2%i==0)m2/=i;
}
}if(m2>1)pr[++cnt]=m2;
now=0x3f3f3f3f,dfs(m,n,0);
cout<<now<<"\n";
}
}
}
bot 的矩阵(matrix)
考场上想复杂了。我是sb
考虑将行和列都看成一个点,建立二分图,如果 ( x , y ) (x,y) (x,y)没有确定那么从左部点 x x x向右部点 y y y连一条边,我们的任务是对每条边确定一个权值,使得左右节点的点权等于与之相连的边权和。
对于一个连通块,其有解的必要条件是左部点 ∑ a i \sum a_i ∑ai=右部点 ∑ b i \sum b_i ∑bi
我们考虑抽取其中一颗生成树,然后自底向上为边赋值,不在生成树上的边权设为 0 0 0
对于已经确定的边,只需让对应的点权减去这个值即可。
复杂度 O ( n 2 ) O(n^2) O(n2)。
考场上想了很蠢的构造。我也是醉了。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
int T,n,m;
ll a[4005],c[2005*2005],sum;
int v[2005*2005],vs[4005];
vector<int>g[4005];
inline ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int has(int x,int y){
return (x-1)*n+y;
}
void dfs(int u){
vs[u]=1;
for(auto v:g[u]){
if(!vs[v]){
dfs(v);
if(u>n)c[has(v,u-n)]=a[v];
else c[has(u,v-n)]=a[v];
a[u]-=a[v];
}
}
}
void solve(){
for(int i=1;i<=2*n;i++){
if(!vs[i]){
dfs(i);
if(a[i]!=0){
cout<<"NoSolution!"<<"\n";
return;
}
}
}cout<<"Botany!"<<"\n";
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<c[has(i,j)]<<' ';
}cout<<"\n";
}
}
int main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
T=read();
while(T--){
n=read(),m=read();
for(int i=1;i<=2*n;i++)a[i]=read(),vs[i]=0,g[i].clear();for(int i=1;i<=n*n;i++)v[i]=c[i]=0;
for(int i=1;i<=m;i++){
int x=read(),y=read();ll z=read();
a[x]-=z,a[n+y]-=z,c[has(x,y)]=z,v[has(x,y)]=1;
}for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!v[has(i,j)])g[i].pb(j+n),g[j+n].pb(i);
}
}solve();
}
}
bot 的殖民扩张(expansion)
好题。
首先考虑一条链的情况, a 1 = 1 , a n = m a_1=1,a_n=m a1=1,an=m
不妨将问题一般化,每个节点都有覆盖半径 d i d_i di,问你能否把 [ 1 , m ] [1,m] [1,m]覆盖完
考虑设 d p i dp_i dpi表示对前 i i i个点定向,能覆盖的最远距离。考虑如下转移:
1.1
1.1
1.1
i
i
i向右边定向,
d
p
i
←
max
(
d
p
i
−
1
,
a
i
+
d
)
[
d
p
i
−
1
≥
a
i
−
1
]
dp_i\gets \max(dp_{i-1},a_i+d)[dp_{i-1}\ge a_i-1]
dpi←max(dpi−1,ai+d)[dpi−1≥ai−1]
1.2
1.2
1.2
i
i
i向左边定向,与一段前缀
j
j
j共同覆盖了
[
1
,
a
i
]
[1,a_i]
[1,ai],此时对于
j
<
k
<
i
j<k<i
j<k<i的点都往右边定向,不妨取一段最小的前缀
j
j
j,
d
p
i
←
max
(
a
i
,
max
j
<
k
<
i
(
a
k
+
d
k
)
)
dp_{i}\gets \max(a_i,\max_{j<k<i}(a_k+d_k))
dpi←max(ai,maxj<k<i(ak+dk))
1.3
1.3
1.3
i
i
i直接继承前
i
−
1
i-1
i−1的最远覆盖距离,
d
p
j
←
d
p
j
−
1
dp_{j}\gets dp_{j-1}
dpj←dpj−1
本题的 d i d_i di是定值,那么首先判断 d p i − 1 dp_{i-1} dpi−1能否覆盖到 i − 1 i-1 i−1,否则判断 d p i − 2 dp_{i-2} dpi−2能否覆盖到 a i − d − 1 a_i-d-1 ai−d−1(这种情况下的最远距离是 max ( a i , a i − 1 + d ) \max(a_i,a_{i-1}+d) max(ai,ai−1+d)),否则判断 d p i − 1 dp_{i-1} dpi−1能否覆盖到 a i − d − 1 a_i-d-1 ai−d−1,否则无解(因为后面一段和前面一段无论如何都接不到一起)
对于环上的情况,考虑从环上距离最大的两个点处断开,记作 L L L。如果 L ≤ d L\le d L≤d显然有解,否则若 a 1 = 1 a_1=1 a1=1往左倒,只需求出 a 2 ∼ n a_{2\sim n} a2∼n能向右扩展的最远距离并判断是否 d p n − a n + d ≥ L dp_n-a_{n}+d\ge L dpn−an+d≥L即可。若 a 1 = 1 a_1=1 a1=1往右倒,那么 a 2 a_2 a2一定往左倒,判断 d − a 2 + 1 + d p n − a n ≥ L d-a_2+1+dp_n-a_n\ge L d−a2+1+dpn−an≥L即可。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
tips:事实上是对两个基本模型的拼接与考察,但是考场上似乎没有想到破环那一步??
我信仰什么,我便实现哪种方法
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define int ll
using namespace std;
int m,n,L,a[1000005],s[1000005],b[1000005],dp[1000005];
bool check(int d){
dp[0]=1;
for(int i=1;i<n;i++){
if(dp[i-1]>=b[i]-1)dp[i]=b[i]+d;
else if(i>=2&&dp[i-2]>=b[i]-d-1)dp[i]=max(b[i],b[i-1]+d);
else if(dp[i-1]>=b[i]-d-1)dp[i]=b[i];
else return 0;
}
return dp[n-1]-b[n-1]+d>=L;
}
bool check2(int d){
if(b[1]-d>=1)return 0;
dp[0]=dp[1]=d+1;
for(int i=2;i<n;i++){
if(dp[i-1]>=b[i]-1)dp[i]=b[i]+d;
else if(i!=2&&dp[i-2]>=b[i]-d-1)dp[i]=max(b[i],b[i-1]+d);
else if(dp[i-1]>=b[i]-d-1)dp[i]=b[i];
else return 0;
}return d-(b[1]-1)+dp[n-1]-b[n-1]>=L;
}
signed main() {
freopen("expansion.in","r",stdin);
freopen("expansion.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>m>>n;
if(n==1){
cout<<m-1;
return 0;
}
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);s[0]=a[0]+m-a[n-1]-1;int mx=0;
for(int i=1;i<n;i++){
s[i]=a[i]-a[i-1]-1;
if(s[i]>s[mx])mx=i;
}b[0]=1;
for(int i=1;i<n;i++){
b[i]=b[i-1]+s[(mx+i)%n]+1;
}L=s[mx];
int l=0,r=L,res=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid)||check2(mid))res=mid,r=mid-1;
else l=mid+1;
}cout<<res;
}
bot 的字符串(palindrome)
想复杂了。
设 f l , r , i f_{l,r,i} fl,r,i表示处理完 [ l , r ] [l,r] [l,r]( r r r必选)右边多了 i ( i ≥ 0 ) i(i\ge 0) i(i≥0)个的方案数, g l , r , i g_{l,r,i} gl,r,i表示处理完 [ l , r ] [l,r] [l,r]( l l l必选)左边多了 i ( i > 0 ) i(i>0) i(i>0)个的方案数。答案是 ∑ i = 1 n f 1 , i , 0 \sum_{i=1}^nf_{1,i,0} ∑i=1nf1,i,0。
简单分析一波。 T T T从中心扩展时只有唯一一种生成方式,也就对应唯一的转移,因此每种方案恰好只会被计算一次。注意到总状态数 O ( n ∑ s i ) O(n\sum{s_i}) O(n∑si),而转移复杂度 O ( 1 ) O(1) O(1),可以通过。
总复杂度 O ( n ∑ s i ) O(n\sum s_i) O(n∑si)。
实现比较复杂。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define ull unsigned long long
using namespace std;
const int mod=1e9+7;
int n;
string s[505];
vector<int>f[505][505],g[505][505];
ull fac[200005];
vector<ull>pre[505],suf[505];
ull gp(int x,int l,int r){
if(l==0)return pre[x][r];
return pre[x][r]-pre[x][l-1]*fac[r-l+1];
}
ull gs(int x,int l,int r){
return suf[x][l]-suf[x][r+1]*fac[r-l+1];
}
void add(int &x,int y){
if((x+=y)>=mod)x-=mod;
}
int main() {
freopen("palindrome.in","r",stdin);
freopen("palindrome.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=1;i<=n;i++)cin>>s[i];
fac[0]=1;for(int i=1;i<=200000;i++)fac[i]=fac[i-1]*1331;
for(int i=1;i<=n;i++){
pre[i].resize(s[i].size()+5);
suf[i].resize(s[i].size()+5);
for(int j=i;j<=n;j++){
f[i][j].resize(s[j].size()+5);
g[i][j].resize(s[i].size()+5);
}
}
for(int i=1;i<=n;i++){
pre[i][0]=s[i][0];
for(int j=1;j<s[i].size();j++){
pre[i][j]=pre[i][j-1]*1331+s[i][j];
}
for(int j=s[i].size()-1;j>=0;j--){
suf[i][j]=suf[i][j+1]*1331+s[i][j];
}
for(int j=0;j<s[i].size();j++){
if(gp(i,0,s[i].size()-j-1)==gs(i,0,s[i].size()-j-1)){
f[i][i][j]=1;
}
}
for(int j=1;j<s[i].size();j++){
if(gp(i,j,s[i].size()-1)==gs(i,j,s[i].size()-1)){
g[i][i][j]=1;
}
}
}
for(int len=2;len<=n;len++){
for(int l=1;l<=n-len+1;l++){
int r=l+len-1;
if(s[l].size()<=s[r].size()&&gs(l,0,s[l].size()-1)==gp(r,0,s[l].size()-1)){
add(f[l][r][s[r].size()-s[l].size()],1);
}
if(s[l].size()>s[r].size()&&gs(l,s[l].size()-s[r].size(),s[l].size()-1)==gp(r,0,s[r].size()-1)){
add(g[l][r][s[l].size()-s[r].size()],1);
}
for(int i=0;i<=s[r].size();i++){
add(f[l][r][i],f[l+1][r][i]);
if(i<s[l].size()&&gs(l,s[l].size()-i,s[l].size()-1)==gp(r,s[r].size()-i,s[r].size()-1)){
add(g[l][r][s[l].size()-i],f[l+1][r][i]);
}
if(i>=s[l].size()&&gs(l,0,s[l].size()-1)==gp(r,s[r].size()-i,s[r].size()-i+s[l].size()-1)){
add(f[l][r][i-s[l].size()],f[l+1][r][i]);
}
}
for(int i=1;i<=s[l].size();i++){
add(g[l][r][i],g[l][r-1][i]);
if(i<=s[r].size()&&gs(l,0,i-1)==gp(r,0,i-1)){
add(f[l][r][s[r].size()-i],g[l][r-1][i]);
}
if(i>s[r].size()&&gs(l,i-s[r].size(),i-1)==gp(r,0,s[r].size()-1)){
add(g[l][r][i-s[r].size()],g[l][r-1][i]);
}
}
}
}
int res=0;
for(int i=1;i<=n;i++){
add(res,f[1][i][0]);
}cout<<res;
}