E. Sequential operations on Sequence
首先,暴力地可以这样递归,时间复杂度 \(O(n^2)\)
inline void solve(int pos,int len,int val){
if(!len||!val)return;
if(pos==0){
for(int i=1;i<=len;i++)
ans[i]+=val;
return;
}int ti=len/l[pos-1];
int rs=len%l[pos-1];
solve(pos-1,l[pos-1],ti*val);
solve(pos-1,rs,val);
}
发现有很多次 \(solve(p,l[p],val)\) 的,于是加一个小优化
if(pos<=lim)cnt[pos-1]+=ti*val;
else solve(pos-1,l[pos-1],ti*val);
for(lim=m;~lim;--lim)
solve(lim,l[lim],cnt[lim]);
这是发现一个小性质,若 \(l_i>l_{i+1}\) 则 \(l_i\) 形同虚设,于是开头一个单调栈
st[++top]=l[1];
for(int i=2;i<=m;i++){
while(top&&st[top]>=l[i])top--;
st[++top]=l[i];
}
由于递归很慢,不妨把第二步的递归改为循环
for(int i=m;i;--i){
int val=cnt[i],pos=i,len=l[i];
while(pos&&len){
cnt[--pos]+=(len/l[pos])*val;
len%=l[pos];
}ans[1]+=val,ans[len+1]-=val;
}
我们考虑这时它慢在那里,可能我们一次取模后,\(len\) 变的很小,于是就要再过很多次才会有贡献。
于是我们一次性跳到有贡献的地方就可以了,由于 \(l\) 单调,我们可以使用 \(upper\_bound\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
int 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 n,m,l[maxn],now,ans[maxn],f[maxn];
inline void solve(int d,int w){
int j=upper_bound(l+1,l+1+m,d)-l-1;
if(!j)ans[1]+=w,ans[d+1]-=w;
else f[j]+=(d/l[j])*w,solve(d%l[j],w);
}
signed main(){
n=read(),m=read();
l[++now]=n;while(m--){
int x=read();
while(now&&l[now]>=x)now--;
l[++now]=x;
}m=now;f[m]=1;
for(int i=m-1;i;--i){
f[i]+=(l[i+1]/l[i])*f[i+1];
solve(l[i+1]%l[i],f[i+1]);
}ans[1]+=f[1];ans[l[1]+1]-=f[1];
for(int i=1;i<=n;i++)
ans[i]+=ans[i-1];
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]);puts("");
return 0;
}
F. Fraction of Fractal
看似十分难做,但是其实只要发现一些性质,还是有一定可做性的,至少不卡科技。
我们发现,只要不存在原图中横向的首尾相接,那么横向的复制就永远不会连起来,竖向同理。
若横竖都有首尾相接的,那么整张图就都是连一起的,答案为 \(1\)。
若无,则整张图不交,设黑点数为 \(cnt\),答案即为 \(cnt^{k-1}\)。
否则要么横交要么竖交,不妨这里讨论横交,竖交同理。
令 \(tot\) 为横向连通的边数(即 \(1\times 2\) 的格子数),\(ud\) 为首尾相接的行数,\(s_i\) 为复制 \(i\) 遍时首尾相接的连通块数。
显然有 \(ans_{i+1}=ans_i *cnt-tot*s_i\),\(s_i=ud*si\),矩阵快速幂优化即可。
讲一下怎么想到行列分开的,因为如果既有行又有列的话会出现环,难以计算答案,如果只有一个的话,直接减去边数即可。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
int 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;
}
#define ll long long
int n,m;ll k;
const int N=1010;
char s[N][N];
int tot[2],ud[2],cnt;
inline int ksm(int x,ll y){
int res=1;
while(y){
if(y&1)res=1ll*res*x%mod;
x=1ll*x*x%mod;y>>=1;
}return res;
}
struct node{
int a[2][2];
inline node(){a[0][0]=a[1][1]=0;a[0][1]=a[1][0]=0;}
inline void init(){a[0][0]=a[1][1]=1;a[0][1]=a[1][0]=0;}
node operator*(const node &x){
node t;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int K=0;K<2;K++)
t.a[i][j]=(t.a[i][j]+1ll*a[i][K]*x.a[K][j])%mod;
return t;
}
}trs,res;
int main(){
scanf("%d%d%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(s[i][j]=='#'){
++cnt;
if(i>1&&s[i-1][j]=='#')++tot[0];
if(j>1&&s[i][j-1]=='#')++tot[1];
}
for(int i=1;i<=n;i++)
if(s[i][1]=='#'&&s[i][m]=='#')++ud[1];
for(int i=1;i<=m;i++)
if(s[1][i]=='#'&&s[n][i]=='#')++ud[0];
if(ud[0]&&ud[1])return puts("1")&0;
if(!ud[0]&&!ud[1])return printf("%d\n",ksm(cnt,k-1))&0;
int op=(ud[1]?1:0);k--;res.init();
trs.a[0][0]=cnt;trs.a[0][1]=0;
trs.a[1][0]=mod-tot[op];trs.a[1][1]=ud[op];
while(k){
if(k&1)res=res*trs;
trs=trs*trs;k>>=1;
}printf("%d\n",(res.a[0][0]+res.a[1][0])%mod);
return 0;
}