• 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;
    }
    
  • 相关阅读:
    使用postMan调用web services wsdl接口
    Python的入门基础(Linux、python、git)
    CrossoverQA文档
    Linux_磁盘分区、挂载、查看
    Linux为什么要挂载
    图解Windows10+优麒麟双系统安装
    Linux 软件安装与卸载
    ventroy 制作多系统启动盘
    字节跳动面试官:请你实现一个大文件上传和断点续传
    关于本博客和博主
  • 原文地址:https://www.cnblogs.com/syzf2222/p/15761051.html
Copyright © 2020-2023  润新知