AGC003 题解

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;
}
posted @ 2022-01-04 00:23  syzf2222  阅读(39)  评论(0编辑  收藏  举报