• 2017国家集训队作业Atcoder题目试做


    2017国家集训队作业Atcoder题目试做

    虽然远没有达到这个水平,但是据说Atcoder思维难度大,代码难度小,适合我这种不会打字的选手,所以试着做一做

    不知道能做几题啊

    在完全自己做出来的题前面打"√“

    计数器菌:11/104

    agc001_d

    如果两个字符确定相等就在中间连一条边,那么所有字符相同就等价于使整个图联通

    然后发现至少要(n-1)条边,而事实上一个序列贡献的边数最大为(frac n 2)条,而且一旦序列里有一个奇数贡献的边数就会减去(frac 1 2),所以如果原始序列出现(gt 2)个奇数,那么就不可行

    一个偶数序列,整体向左平移一个之后,正好全部连起来了

    如果有奇数怎么办?因为至多两个奇数,我们把奇数放到两边,中间全是偶数,那么可以像刚才那样做,两边的奇数这样做也符合题意。

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=200;
    int n,m;
    int a[maxn];
    
    int main(){
    #ifdef LZT
    //	freopen("in","r",stdin);
    #endif
    	int num=0;
    	n=read();m=read();
    	rep(i,1,m){
    		a[i]=read();
    		if(a[i]&1) num++;
    	}
    	if(num>2){
    		puts("Impossible");
    		return 0;
    	}
    	for(int i=1;i<=m;i++)
    		if(a[i]&1){
    			if(a[1]&1) swap(a[i],a[m]);
    			else swap(a[i],a[1]);
    		}
    	rep(i,1,m) cout<<a[i]<<' ';
    	cout<<endl;
    	a[1]++;a[m]--;
    	if(a[m]==0) m--;
    	if(m>=2){
    		cout<<m<<endl;
    		rep(i,1,m) cout<<a[i]<<' ';
    		cout<<endl;
    	}
    	else{
    		if(n<=2){
    			cout<<1<<endl;
    			cout<<n<<endl;
    		}
    		else{
    			cout<<2<<endl;
    			cout<<n-1<<' '<<1<<endl;
    		}
    	}
    	return 0;
    }
    

    agc001_e

    我们发现答案其实就是要求(sum_{i=1}^{n-1}sum_{j=i+1}^nC_{a_i+a_j+b_i+b_j}^{a_i+a_j})

    然后知道(C_{a_i+a_j+b_i+b_j}^{a_i+a_j})实际上就是点((-a_i,-b_i))走到((a_j,b_j))的方案数

    那么原式等价于求点集((-a_i,-b_i))到点集((a_i,b_i))两两的方案数的和减去所有点走到他对应的对称点的方案数(即(i=j)的方案数)除以2(每个方案被算了两次)

    所以dp就可以了,可以想象中建立一个超级源点连向所有的((-a_i,-b_i))和超级汇点连向所有的((a_i,b_i)),就可以求出方案数

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int mod=1000000007;
    const int maxn=200200;
    const int maxm=2200;
    int n;
    int a[maxn],b[maxn];
    int dp[maxm*2][maxm*2];
    int flag[maxm*2][maxm*2];
    int ans;
    
    void pl(int &a,ll b){
    	a=(a+b%mod)%mod;
    }
    void mi(int &a,ll b){
    	a=a-b%mod;
    	while(a<0) a+=mod;
    	a=a%mod;
    }
    
    int main(){
    #ifdef LZT
    	freopen("in","r",stdin);
    #endif
    	n=read();
    	rep(i,1,n) a[i]=read(),b[i]=read();
    	rep(i,1,n){
    		flag[2100-a[i]][2100-b[i]]++;
    		flag[a[i]+2100][b[i]+2100]++;
    	}
    	rep(i,1,4200){
    		rep(j,1,4200){
    			pl(dp[i][j],dp[i-1][j]);
    			pl(dp[i][j],dp[i][j-1]);
    			if(flag[i][j] && i<=2100 && j<=2100) pl(dp[i][j],flag[i][j]);
    			if(flag[i][j] && i>=2100 && j>=2100) pl(ans,dp[i][j]*1ll*flag[i][j]);
    		}
    	}
    	memset(dp,0,sizeof(dp));
    	dp[0][0]=1;
    	rep(i,0,4200){
    		rep(j,0,4200){
    			if(i==0 && j==0) continue;
    			if(i) pl(dp[i][j],dp[i-1][j]);
    			if(j) pl(dp[i][j],dp[i][j-1]);
    		}
    	}
    	rep(i,1,n)
    		mi(ans,dp[a[i]+a[i]][b[i]+b[i]]);
    	ans=ans*500000004ll%mod;
    	cout<<ans<<endl;
    	return 0;
    }
    

    agc002_d

    先考虑暴力做法,对于一组询问((x,y,z)),我们暴力将边从小到大加入图里,当(x)所在的连通块点数加(y)所在连通块点数(当(x)(y)在不同连通块时才加)第一次(geq z)时,当前边的序号就是答案

    所以答案是有单调性的,可以二分

    然后每一组都二分肯定不行,所以整体二分

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=100100;
    int n,m,Q;
    pii edge[maxn];
    struct query
    {
    	int ind;
    	int a,b,num;
    	void re(int x){
    		a=read(),b=read(),num=read();
    		ind=x;
    	}
    } q[maxn],tmp[maxn];
    int ans[maxn],fa[maxn],sz[maxn];
    bool ok[maxn];
    pii sta[maxn];int cnt;
    
    inline int fp(int x){if(x==fa[x]) return x;return fp(fa[x]);}
    
    void solve(int l,int r,int le,int ri){
    	//cout<<l<<' '<<r<<' '<<le<<' '<<ri<<endl;
    	if(l==r){
    		rep(i,le,ri) ans[q[i].ind]=l;
    		int x=edge[l].fi,y=edge[l].se;
    		x=fp(x);y=fp(y);
    		if(x!=y){
    			if(sz[x]>sz[y])swap(x,y);
    			fa[x]=y;sz[y]+=sz[x];
    		}
    		return;
    	}
    	int md=(l+r)>>1;cnt=0;
    	rep(i,l,md){
    		int x=edge[i].fi,y=edge[i].se;
    		x=fp(x);y=fp(y);
    		if(x!=y){
    			if(sz[x]>sz[y]) swap(x,y);
    			fa[x]=y;sz[y]+=sz[x];
    			sta[++cnt]=mp(x,y);
    		}
    	}
    	rep(i,le,ri){
    		query &nw=q[i];
    		int a=nw.a,b=nw.b;
    		//cout<<a<<' '<<b<<endl;
    		a=fp(a);b=fp(b);
    		//cout<<i<<' '<<a<<' '<<b<<' ';
    		int nww=0;
    		if(a==b) nww=sz[a];else nww=sz[a]+sz[b];
    		if(nww>=nw.num) ok[i]=1;else ok[i]=0;
    		//cout<<ok[i]<<endl;
    	}
    	int pos=le-1;
    	rep(i,le,ri)
    		if(ok[i]) tmp[++pos]=q[i];
    	pos=ri+1;
    	rrep(i,ri,le)
    		if(!ok[i]) tmp[--pos]=q[i];
    	//cout<<le<<' '<<ri<<' '<<pos<<endl;
    	rep(i,le,ri) q[i]=tmp[i];
    	while(cnt){
    		int x=sta[cnt].fi,y=sta[cnt].se;
    		fa[x]=x;sz[y]-=sz[x];cnt--;
    	}
    	solve(l,md,le,pos-1);solve(md+1,r,pos,ri);
    }
    
    int main(){
    	n=read(),m=read();
    	rep(i,1,m) edge[i].fi=read(),edge[i].se=read();
    	Q=read();
    	rep(i,1,Q) q[i].re(i);
    	rep(i,1,n) sz[i]=1,fa[i]=i;
    	solve(1,m,1,Q);
    	rep(i,1,Q) printf("%d
    ",ans[i]);
    	return 0;
    }
    
    /*
    5 6
    2 3
    4 5
    1 2
    1 3
    1 4
    1 5
    6
    2 4 3
    2 4 4
    2 4 5
    1 3 3
    1 3 4
    1 3 5
    */
    

    agc002_e

    真难想的博弈题

    首先先想状态

    不知怎么想到把他表示成图形

    就是我们先排序 然后把一堆石子想象成一个石子个数*1的矩形。 把矩形从高到低排列变成一个图形。

    然后操作就变成了删掉最左边一列或者最下面一行

    假设有一个点当前在((1,1)),那么每次操作他向右或者向上移动一个,不能移动者输

    那么给每个点标记上(o)或者(x),分别表示必胜和必败

    所有最外层的角上(意会)一定都是(x)

    然后发现当((x+1,y+1))不是最外层的点的时候,((x,y))((x+1,y+1))的标记相同

    所以算法就是先把((1,1))向右上方移动直到边界为止,然后要么向上要么向右,如果都是必败那么就是必败,否则必胜

    向上向右因为只有一个方向所以只奇偶性有关

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=100100;
    int n;
    int a[maxn];
    
    int main(){
    	n=read();
    	rep(i,1,n) a[i]=read();
    	sort(a+1,a+n+1);
    	reverse(a+1,a+n+1);
    	rep(i,1,n+1){
    		if(a[i]<i){
    			i--;
    			int num=0;
    			for(int j=i+1;j<=n;j++)
    				if(a[j]==i) num++;
    			if(num&1){
    				puts("First");
    				return 0;
    			}
    			if((a[i]-i)&1) puts("First");
    			else puts("Second");
    			return 0;
    		}
    	}
    }
    

    agc002_f

    直接( ext{dp})

    把“把每种颜色的最左边的球变为0”理解成“任意前缀的颜色数<0的数量”

    然后放球的时候一次把一种颜色的球放完,把“放在后面”变成“插入这么多个新的球”,放完一种颜色的 (k-1) 个球之后才能放 (0)

    然后 (f[i][j]) 表示当前放前 (i) 种颜色(我们固定先放颜色1,再放颜色2,最后乘上 ( ext{fac[i]}) 就可以了),还没放的 ( ext{0}) 的个数是 (j) 的方案数

    那么首先可以在最前面加一个 (0) ,即 (f[i][j]=f[i][j+1])

    然后可以新增一种颜色,从 (f[i-1][j-1]) 转移过来,我们知道当前的球的个数是 ((i-1)*k-(j-1)) 个,要放入 (k-1) 个新的球,运用插板法,就是 (C_{(i-1)*k-(j-1)+k-1-1}^{(i-1)*k-j}=C_{i*k-j-1}^{k-2}) 种方案

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int mod=1e9+7;
    const int maxn=3050;
    int n,k;
    ll f[maxn][maxn];
    ll fac[maxn*maxn],inv[maxn*maxn];
    
    inline int C(int x,int y){
    	if(x<y) return 0;
    	return fac[x]*inv[y]%mod*inv[x-y]%mod;
    }
    
    int main(){
    	n=read(),k=read();
    	if(k==1){
    		puts("1");
    		return 0;
    	}
    	fac[0]=1;
    	rep(i,1,3000*3000) fac[i]=fac[i-1]*i%mod;
    	inv[0]=inv[1]=1;
    	rep(i,2,3000*3000) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    	rep(i,1,3000*3000) inv[i]=inv[i]*inv[i-1]%mod;
    	f[0][0]=1;
    	rep(i,1,n){
    		rrep(j,i,0){
    			f[i][j]=f[i][j+1]%mod;
    			if(j){
    				f[i][j]+=f[i-1][j-1]*C(i*k-j-1,k-2)%mod;
    				f[i][j]%=mod;
    			}
    		}
    	}
    	cout<<f[n][0]*fac[n]%mod;
    	return 0;
    }
    

    agc003_d

    首先把每个数分解质因数,然后把指数模3,称变换之后的数为原来的数的最简数

    对于一个最简数,存在一个补数,定义为与最简数相乘是立方数的最小数

    对于一个最简数代表的数集和其补数代表的数集,我们选取较大的一个

    如果一个数和其补数相同,那么只能选1个

    关键在于分解质因数,我们先筛出来3000以内的质数,然后如果一个数把3000以下的数除完之后不为1,那么肯定是完全平方数或者质数,分别判断一下即可(如果是两个3000以上的质数相乘,那么不可能对答案产生贡献,所以可以忽略)

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<ll,ll> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register ll i=(ll)(j);i<=(ll)(k);i++)
    #define rrep(i,j,k) for(register ll i=(ll)(j);i>=(ll)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const ll maxn=100100;
    int n;
    ll a[maxn];
    bool isp[maxn];
    int pr[maxn],cnt;
    int num[maxn][500];
    set<ll> s;
    map<ll,int> M;
    map<ll,int> ind;
    map<ll,int> used;
    
    void init(){
    	memset(isp,1,sizeof(isp));
    	isp[0]=isp[1]=0;
    	rep(i,2,3000){
    		if(isp[i]){
    			pr[++cnt]=i;
    		}
    		for(ll j=1;j<=cnt && i*pr[j]<=3000;j++){
    			isp[i*pr[j]]=0;
    			if(i%pr[j]==0) break;
    		}
    	}
    }
    
    bool pd(ll x){
    	for(ll i=2;i*i<=x;i++){
    		if(x%i==0) return 0;
    	}
    	return 1;
    }
    
    int main(){
    #ifdef LZT
    	//freopen("in","r",stdin);
    #endif
    	ll ans=0;
    	n=read();
    	rep(i,1,n) a[i]=read();
    	init();
    	rep(i,1,n){
    		ll t=a[i];
    		rep(j,1,cnt){
    			while(t%pr[j]==0){
    				t=t/pr[j];
    				num[i][j]++;
    			}
    			num[i][j]%=3;
    		}
    		if(t>pr[cnt]){
    			ll nw=sqrt(t);
    			if(nw*nw==t){
    				num[i][cnt+1]=nw;
    				num[i][cnt+2]=2;
    				t=1;
    			}
    			else{
    				if(t<=100000 && pd(t)){
    					num[i][cnt+1]=t;
    					num[i][cnt+2]=1;
    					t=1;
    				}
    			}
    		}
    		ll tt=1;
    		rep(j,1,cnt){
    			if(num[i][j]==1) tt=tt*pr[j];
    			else if(num[i][j]==2) tt=tt*pr[j]*pr[j];
    		}
    		if(num[i][cnt+1]){
    			tt=tt*num[i][cnt+1];
    			if(num[i][cnt+2]==2) tt=tt*num[i][cnt+1];
    		}
    		if(t==1){
    			s.insert(tt);
    			M[tt]++;
    			ind[tt]=i;
    		}
    		else ans++;
    	}
    	for(set<ll>::iterator it=s.begin();it!=s.end();it++){
    		ll nw=*it;
    		if(used[nw]) continue;
    		used[nw]=1;
    		ll i=ind[nw];
    		ll t=1;
    		for(ll j=1;j<=cnt;j++){
    			if(num[i][j]==1) t=t*pr[j]*pr[j];
    			else if(num[i][j]==2) t=t*pr[j];
    		}
    		if(num[i][cnt+1]){
    			if(num[i][cnt+2]==1) t=t*num[i][cnt+1]*num[i][cnt+1];
    			else t=t*num[i][cnt+1];
    		}
    		if(nw==t) ans++;
    		else ans+=max(M[nw],M[t]);
    		used[t]=1;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    agc003_e

    借鉴了( ext{fizzydavid})的代码,写的很精巧

    题目就是说有一个数字串S,初始长度为n,是1 2 3 4 …… n,有m次操作,每次操作给你一个正整数a[i],你先把S无穷重复,然后把前a[i]截取出来成为新的S。
    求m次操作后,每个数字在S中出现的次数。

    我们倒着考虑

    首先如果 (a[i] ge a[i+1]) 那么 (a[i]) 可以直接删掉,显然没用

    所以我们先用单调栈把 (a) 变成单调递增的

    然后每一次操作等价于把前一次的序列复制几次然后加上一个前缀

    我们倒着考虑,用一个 ( ext{pair}) 表示我们当前给长度为 ( ext{pair.first}) 的前缀重复加了 ( ext{pair.second})

    那么一开始,考虑最后一次操作,等价于插入 ( ext{pair<a[cnt],1>}) 就是整个加了1次

    然后考虑当前操作的长度为 (cur) ,对于每一个存在的 ( ext{pair}) ,我们会将其更新,因为实际上这个 (pair) 可以由当前的 ( ext{cur}) 得到,所以我们计算当前的 (cur) 这个前缀在每一个存在的 ( ext{pair}) 中一共出现几次,也就是

    sum+=nw.se*(nw.fi/cur);

    当然如果 (nw.fi) 不是(cur) 的倍数,那么会出现一个多余的小于 (cur) 的前缀,这个前缀出现了 (nw.se) 次,长度就是 ( ext{nw.fi%cur})

    然后我们使用partial sum的方法,最后倒着加起来就好了

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    priority_queue<pair<ll,ll> > pq;
    ll n,q;
    ll st[100100],cnt;
    ll ans[100100];
    int main(){
    #ifdef LZT
    //	freopen("in","r",stdin);
    #endif
    	n=read(),q=read();
    	st[++cnt]=n;
    	for(int i=1;i<=q;i++){
    		ll x=read();
    		while(st[cnt]>=x) cnt--;
    		st[++cnt]=x;
    	}
    	pq.push(mp(st[cnt],1));
    	for(int i=cnt;i>=1;i--){
    		ll sum=0,cur=st[i];
    		while(!pq.empty() && pq.top().fi>cur){
    			pair<ll,ll> nw=pq.top();pq.pop();
    			sum+=nw.se*(nw.fi/cur);
    			if(nw.fi%cur) pq.push(mp(nw.fi%cur,nw.se));
    		}
    		pq.push(mp(cur,sum));
    	}
    	while(!pq.empty()) ans[pq.top().fi]+=pq.top().se,pq.pop();
    	rrep(i,n,1) ans[i]+=ans[i+1];
    	rep(i,1,n) printf("%lld 
    ",ans[i]);
    	return 0;
    }
    

    agc003_f

    ( ext{Atcoder}) 的题果然都是神仙

    (left{ egin{matrix} b & c \ 0 & d end{matrix} ight} ^ {k-1}) 之后 (b-c) 就是答案

    下面来分析一波:

    首先定义 纵向相邻横向相邻

    纵向相邻 指一个初始图案存在一列,满足这一列的第一行和最后一行两个格子都是 #

    类似的,横向相邻 指一个初始图案存在一行,满足这一行的第一列和最后一列两个格子都是 #

    不难发现,如果一个初始图案既 纵向相邻横向相邻,那么他复制多次之后仍旧联通

    如果一个初始图案既不 纵向相邻 也不 横向相邻,那么他复制多次之后的联通块数就是黑色格子数的 (k-1) 次,因为复制之后不会有连通块合并的情况

    剩下的就是只满足其中一个的情况。不失一般性,我们假设初始图案满足 纵向相邻,不满足 横向相邻

    考虑当前在 (k-1) 层,我们操作一次之后到第 (k) 层,中间每个量的变化

    (a_{k}) 表示第 (k) 层的连通块个数,(b_k) 表示第 (k) 层的黑色格子数量,(c_k) 表示第 (k) 层的图中有多少个上下相邻且都为黑色的格子对,(d_k) 表示第 (k) 层的图中有多少满足 纵向相邻 的列

    那么我们来考虑 (k-1)(k) 的变化

    (a_k=b_{k-1}-c_{k-1},b_k=b_{k-1}^2,c_k=b_{k-1} cdot c_{k-1}+c_{k-1} cdot d_{k-1},d_k=d_{k-1}^2)

    惊讶的发现,他可以用一个矩阵完美地套进去!

    首先 (a) 这个玩意根本没有用,不记录

    考虑 (2 imes 2) 的矩阵乘法

    (left{matrix{a & b\c&d} ight}^2=left{matrix{a^2+bc & ab+bd\ac+cd & bc+d^2} ight})

    那么

    (left{matrix{b_{k-1} & c_{k-1}\0&d_{k-1}} ight}^2=left{matrix{b_{k-1}^2 & b_{k-1} cdot c_{k-1}+c_{k-1} cdot d_{k-1}\0 & d_{k-1}^2} ight}=left{matrix{b_k & c_k \ 0 & d_k} ight})

    然后就做完了。。。

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=1010;
    const int mod=1e9+7;
    int n,m;ll k;
    char c[maxn][maxn];
    char tc[maxn][maxn];
    
    struct Matrix{
    	int a[3][3];
    	Matrix(){memset(a,0,sizeof(a));}
    	void init_one(){a[1][1]=a[2][2]=1;}
    	Matrix operator * (const Matrix &b) const{
    		Matrix ret;
    		rep(i,1,2) rep(j,1,2) rep(k,1,2){
    			ret.a[i][j]+=(a[i][k]*1ll*b.a[k][j])%mod;
    			if(ret.a[i][j]>=mod) ret.a[i][j]-=mod;
    		}
    		return ret;
    	}
    	void pr(){
    		cout<<a[1][1]<<' '<<a[1][2]<<endl<<a[2][1]<<' '<<a[2][2]<<endl<<endl;
    	}
    } a;
    
    Matrix ksm(Matrix a,ll p){
    	Matrix ret;ret.init_one();
    	while(p){
    		if(p&1) ret=ret*a;
    		p>>=1;
    		a=a*a;
    	}
    	return ret;
    }
    
    int ksm(int x,ll p){
    	int ret=1;
    	while(p){
    		if(p&1) ret=ret*1ll*x%mod;
    		p>>=1;
    		x=x*1ll*x%mod;
    	}
    	return ret;
    }
    
    void work(){
    	n=read(),m=read(),k=read();
    	int cnt=0;bool f1=0,f2=0;
    	rep(i,1,n) rep(j,1,m){
    		c[i][j]=getchar();
    		while(c[i][j]!='.' && c[i][j]!='#') c[i][j]=getchar();
    	}
    	rep(i,1,n) if(c[i][1]=='#' && c[i][m]=='#') f1=1;
    	rep(j,1,m) if(c[1][j]=='#' && c[n][j]=='#') f2=1;
    	if(f2 && !f1){
    		rep(i,1,n) rep(j,1,m) tc[j][n+1-i]=c[i][j];
    		swap(n,m);
    		rep(i,1,n) rep(j,1,m) c[i][j]=tc[i][j];
    	}
    	rep(i,1,n){
    		if(c[i][1]=='#' && c[i][m]=='#') a.a[2][2]++;
    		rep(j,1,m){
    			if(c[i][j]=='#') a.a[1][1]++;
    			if(c[i][j]=='#' && c[i][j-1]=='#') a.a[1][2]++;
    		}
    	}
    	if(!f1 && !f2){
    		printf("%d
    ",ksm(a.a[1][1],k-1));
    		return;
    	}
    	else if(f1 && f2){
    		puts("1");
    		return;
    	}
    	a=ksm(a,k-1);
    	printf("%d
    ",(a.a[1][1]-a.a[1][2]+mod)%mod);
    }
    
    int main(){
    	#ifdef LZT
    		freopen("in","r",stdin);
    	#endif
    	
    	work();
    	
    	#ifdef LZT
    		Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
    	#endif
    }
    

    √agc004_c

    去年南外校赛原题,当时不会。。。o(╥﹏╥)o

    直接构造,第一列涂红,最后一列涂蓝,剩下的格子奇数行涂红,偶数行涂蓝,原来是紫色的格子都涂上,一定可以

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=550;
    int n,m;
    char c[maxn][maxn];
    
    void work(){
    	n=read(),m=read();
    	rep(i,1,n) rep(j,1,m){
    		c[i][j]=getchar();
    		while(c[i][j]!='.' && c[i][j]!='#') c[i][j]=getchar();
    	}
    	rep(i,1,n){
    		c[i][1]='R',c[i][m]='B';
    		if(i&1){rep(j,2,m-1) if(c[i][j]=='.') c[i][j]='R';}
    		else{rep(j,2,m-1) if(c[i][j]=='.') c[i][j]='B';}
    	}
    	rep(i,1,n){
    		rep(j,1,m) if(c[i][j]!='B') putchar('#'); else putchar('.');
    		puts("");
    	}
    	puts("");
    
    	rep(i,1,n){
    		rep(j,1,m) if(c[i][j]!='R') putchar('#'); else putchar('.');
    		puts("");
    	}
    }
    
    int main(){
    	#ifdef LZT
    		freopen("in","r",stdin);
    	#endif
    	
    	work();
    	
    	#ifdef LZT
    		Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
    	#endif
    }
    

    √agc004_d

    水题,不难发现 (1) 一定要连到 (1),然后根据题目,输入是一棵树,那么可以 ( ext{dfs}) 一遍,一旦这个点为根的子树内有一个点的深度 (geq k),那么这个点必须连到 (1),统计一下即可

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=100100;
    int n,k,ans,a[maxn];
    vector<int> G[maxn];
    
    int dfs(int u){
    	int nw=0;
    	for(int i=0;i<G[u].size();i++){
    		int v=G[u][i];
    		nw=max(nw,dfs(v));
    	}
    	nw++;
    	if(nw==k && a[u]!=1) ans++,nw=0;
    	return nw;
    }
    
    void work(){
    	n=read(),k=read();
    	rep(i,1,n){
    		int x=read();
    		a[i]=x;
    		if(i==1){
    			if(x!=1) ans++;
    			a[i]=1;
    		}
    		else G[x].pb(i);
    	}
    	dfs(1);
    	cout<<ans<<endl;
    }
    
    int main(){
    	#ifdef LZT
    		freopen("in","r",stdin);
    	#endif
    	
    	work();
    	
    	#ifdef LZT
    		Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
    	#endif
    }
    

    agc004_e

    看题解比较好

    如果看完了题解的前一半,知道了 (E) 移动的矩形可以推出边界矩形,那么就 (f[i][j][a][b]) 表示当前 (E) 的矩形是 ((i,j))((p,q)) 时最大答案(用 ( ext{short}) 存或者滚动),那么枚举四个方向,判断能否扩展,更新一下即可,具体见程序

    #include<stdio.h>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<string>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair<long long,long long> pll;
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define rep(i,j,k)  for(register int i=(int)(j);i<=(int)(k);i++)
    #define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
    #define Debug(...) fprintf(stderr, __VA_ARGS__)
    
    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*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    const int maxn=102;
    int n,m;
    short f[maxn][maxn][maxn][maxn];
    char s[maxn][maxn];
    short a[maxn][maxn];
    
    short ask(int X1,int Y1,int X2,int Y2){
    	if(X2<X1 || Y2<Y1) return 0;
    	return a[X2][Y2]-a[X1-1][Y2]-a[X2][Y1-1]+a[X1-1][Y1-1];
    }
    
    void work(){
    	n=read(),m=read();
    	int x=-1,y=-1;
    	rep(i,1,n){
    		scanf("%s",s[i]+1);
    		rep(j,1,m){
    			if(s[i][j]=='E'){
    				x=i;y=j;
    			}
    			a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+(s[i][j]=='o');
    		}
    	}
    	rrep(i,x,1) rrep(j,y,1) rep(p,x,n) rep(q,y,m){
    		short &nw=f[i][j][p][q];
    		short le=max(p-x+1,i),ri=min(p,n-x+i);
    		short up=max(q-y+1,j),down=min(q,m-y+j);
    		if(i>1){
    			short ad=0;
    			if(p-x<i-1) ad=ask(i-1,up,i-1,down);
    			f[i-1][j][p][q]=max(f[i-1][j][p][q],(short)(nw+ad));
    		}
    		if(j>1){
    			short ad=0;
    			if(q-y<j-1) ad=ask(le,j-1,ri,j-1);
    			f[i][j-1][p][q]=max(f[i][j-1][p][q],(short)(nw+ad));
    		}
    		if(p<n){
    			short ad=0;
    			if(x-i+p+1<=n) ad=ask(p+1,up,p+1,down);
    			f[i][j][p+1][q]=max(f[i][j][p+1][q],(short)(nw+ad));
    		}
    		if(q<m){
    			short ad=0;
    			if(y-j+q+1<=m) ad=ask(le,q+1,ri,q+1);
    			f[i][j][p][q+1]=max(f[i][j][p][q+1],(short)(nw+ad));
    		}
    	}
    	printf("%d
    ",f[1][1][n][m]);
    }
    
    int main(){
    	#ifdef LZT
    		freopen("in","r",stdin);
    	#endif
    	
    	work();
    	
    	#ifdef LZT
    		Debug("My Time: %.3lfms
    ", (double)clock() / CLOCKS_PER_SEC);
    	#endif
    }
    
  • 相关阅读:
    zoj 3279 线段树 OR 树状数组
    fzu 1962 树状数组 OR 线段树
    hdu 5057 块状链表
    hdu3487 Play with Chain
    bzoj 1588营业额统计(HNOI 2002)
    poj2823 Sliding Window
    poj2828 Buy Tickets
    poj2395 Out of Hay
    poj3667 Hotel
    poj1703 Lost Cows
  • 原文地址:https://www.cnblogs.com/wawawa8/p/9716143.html
Copyright © 2020-2023  润新知