AGC003[BCDEF]题解

2018-12-28 有点累EF明天再写叭=v=

2018-12-29 update EF

B - Simplified mahjong

可以注意到 一段连续的非0序列都可以凑出\lfloor\frac{\sum a_i}{2} \rfloor

就是显然%2=0的可以内部配完 然后%2=1的可以随便向两边传递这个1(就是和旁边的配对改变自己和配对的奇偶性从而使自己变成%2=0旁边变成%2=1)于是就按照0分割统计答案就行了qwq

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mxn 100010
using namespace std;

ll a[mxn],n;

int main()
{
	scanf("%lld",&n);ll ans=0;
	for(int i=1;i<=n;i++)	scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
	{
		ll cnt=a[i];
		while(i<n && a[i+1]!=0)	cnt+=a[i+1],i++;
		ans+=cnt/2;
	}
	printf("%lld\n",ans);
	return 0;
}

C - BBuBBBlesort!

两种操作 翻转长度为2 翻转长度为3

我们用dis[i]表示排名第i的和它所在的初始的位置的距离

翻转长度为2的可以改变一对的奇偶性 然后长度为3的不改变奇偶性

然后把奇数的个数加起来/2就好啦=v=

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mxn 100100
using namespace std;

int a[mxn],n,id[mxn];
bool cmp(int x,int y)
{
	return a[x]<a[y];
}
int main()
{
	scanf("%d",&n);ll ans=0;
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]),id[i]=i;
	sort(id+1,id+n+1,cmp);
	for(int i=1;i<=n;i++)	ans+=abs(id[i]-i)&1;
	printf("%lld\n",ans/2);
	return 0;
}

D - Anticube

我再读错题我就是彪头[flag] = =

读成了选择一段区间 然后思考一下午不会做= =

看题解发现md是随便选几个

这样的话就每个数质因数分解 然后指数全部%3变成最小表示 求一下他的补集 就是 所有指数变成(3-x)%3

这样的话贪心选 自己或补集就可以辣 注意补集=自己只能选一个

发现 ai 10e10 (上pollard-rho啊)

我们可以筛一下10e10的1/3次方以内的质数 然后分解一下

剩下的一定是一个大质数的平方或者大质数

直接判一下就好了=v=

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define inf 20021225
#define ll long long
#define mxn 100100
using namespace std;

map<ll,int> s;
map<ll,bool> h;
bool isp[10100];
ll p[1310],cnt;
void get(int n)
{
	isp[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!isp[i])	p[++cnt]=i;
        for(int j=1;j<=cnt&&p[j]*i<=n;j++)
        {
            isp[p[j]*i]=1;
            if(i%p[j]==0)	break;
        }
    }
}
ll a[mxn],b[mxn]; int n;
int main()
{
	get(10000);// printf("%d\n",cnt);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		ll q=a[i]; a[i]=b[i]=1ll;
		for(int j=1;j<=cnt;j++)
		{
			int qwq=0;
			if(q==1)	break;
			if(q%p[j]==0)
			{
				while(q%p[j]==0)
				{
					q/=p[j];
					qwq++;
				}
				qwq%=3;
				if(qwq==1)	b[i]*=(ll)p[j]*p[j],a[i]*=p[j];
				if(qwq==2)	b[i]*=p[j],a[i]*=(ll)p[j]*p[j];
			}
		}
		if(q!=1)
		{
			ll x=(ll)sqrt(q);
			if(x*x == q)	b[i]*=x,a[i]*=x*x;
			else	b[i]*=q*q,a[i]*=q;
		}
	}
	for(int i=1;i<=n;i++)	s[a[i]]++;
	int	ans=0;// printf("%d\n",ans);
	for(int i=1;i<=n;i++)
	{
		if(!h[a[i]])
		{
			h[a[i]]=h[b[i]]=1;
			if(a[i]==b[i])	ans++;
			else	ans+=max(s[a[i]],s[b[i]]);
		}
	}
	printf("%d\n",ans);
	return 0;
}

EF明天更!(两道神题想起来写起来都很烦= =)

 

E - Sequential operations on Sequence

神仙题做不来

首先可以得到一个结论 a[i]>=a[i+1]的时候只有a[i+1]是有用的 那么我们可以通过单调栈来维护就可以剔除掉没有用的部分

接下来我们来讲神仙操作

我们将操作序列倒过来 倒着进行推导

首先 令s = a[i+1] / a[i] 那么所有的数一定是有一个s倍的增加 然后 r=a[i+1]%a[i] 就是前r个会多出一个零头

对于维护 我们利用两个数组 add 和 mul 分别维护个体增长和倍数级的增长

那么我们可以通过递归来实现 solve(s,l) -> solve(s%st[pos],l*s/st[pos]) 来表示零头和倍数级

那么我们可以通过二分来找到第一个<s的st[pos]那么就可以将这个过程优化到log

然后注意到s每次%一个小于s的数 这样的话最多只会进行log次[类似辗转相除]

这样的话 我们对于一次处理的复杂度是O(lgslgn)

一共n次操作 就是O(nlgnlgs)啦

然后处理的时候 add 是对于每一个位置的一个差分数组 那么最后就是要前缀和啦 mul就是对于每一次操作的每一个位置增长的倍数数组 那么第一次的倍数数组当然是 mul[1] 啦

由于第一次 每一个位置 对应的都是i 每一个数只出现一次 所以就是mul整体 add就是前缀和

最后就是 add的前缀和 + mul[1] 就是答案咯

代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mxn 100010
using namespace std;

ll a[mxn],stk[mxn];int tp,n,q;
ll cnt[mxn],add[mxn],mul[mxn];

int erf(int l,int r,ll x)
{
	int pos=r;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(stk[mid] <= x)	pos=mid,l=mid+1;
		else	r=mid-1; 
	}
	return pos;
}

void work(ll l,ll k,int x)
{
	if(!k)	return;
	if(k<=stk[1])
	{
		add[1] += l;
		add[k+1] -= l;
		return;
	}
	int pos=erf(1,x,k);
	ll tmp = k / stk[pos];
	mul[pos] += tmp*l;
	work(l,k%stk[pos],pos-1);
}

int main()
{
	scanf("%d%d",&n,&q);
	stk[++tp] = n;
	for(int i=1;i<=q;i++)
	{
		scanf("%lld",&a[i]);
		while(tp && stk[tp] >= a[i])	tp--;
		stk[++tp] = a[i];
	}
	mul[tp] = 1;
	for(int i=tp;i>=2;i--)
	{
		ll tmp = stk[i]/stk[i-1];
		ll fin = stk[i]%stk[i-1];
		mul[i-1] += mul[i]*tmp;
		work(mul[i],fin,i-1);
	}
	for(int i=1;i<=stk[1];i++)
	{
		add[i] += add[i-1];
		printf("%lld\n",mul[1]+add[i]);
	}
	for(int i=stk[1]+1;i<=n;i++)
		printf("0\n");
	return 0;
}

F在路上了qwq

F - Fraction of Fractal

非常nb的一个题qwq(才不会说我题意理解了半天= =题意不理解的可以去loj看中文题面会好很多= =)

看了题解才会做 简直是神仙

我们对于一行(或一列)的两端都是黑色的计数分别为 lr ud 对所有的黑色格子计数为cnt

然后会出现4种情况

1.lr>0&&ud>0 这个时候必定是全部联通的 因为原图中保证了四联通 而每一个格子和上下左右是都能联通的 答案是1

2.lr=0&&ud=0 这个时候必定是全部不连通 所以答案是 cnt^(k-1)

3.lr>0&&ud=0 4.lr=0&&ud>0

这两种情况是等价的 因为我们可以把其中一个旋转45°来转换成另一种情况=v=

我们在这里只讨论 ud>0

ud>0那么就是整个图形会有一些上下联通的块把它们连接起来 整个图形就是由一些链组成的

这样的时候我们怎么计数呢?

显然 链是树 所以我们可以根据森林的性质 树数=点数-边数

这样的话我们只需要分开统计链和点就可以了

点很好计数 每次一个黑色格子变成一个黑色联通块 是指数级增长的 就是cnt^(k-1)

边怎么统计呢 这就用到神仙做法了

我们再来统计一个上下接口 就是(a[i-1][j]和a[i][j])都是黑色的情况 计数为eud 这个时候它们之间是会有一条边的

然后我们可以列出柿子 Ek = Ek-1 * cnt + eud*ud^(k-2) 边界条件就是E2=eud

因为 每一个k-1分形里都有ud^(k-2)个联通的1阶分形然后*eud条新边就可以了=v=

然后这个东西构造一波矩阵乘法就可以了w

\begin{bmatrix} E_{k-1} & eud*ud^{k-2} \end{bmatrix} * \begin{bmatrix} cnt &0 \\ 1& ud\end{bmatrix} =\begin{bmatrix} E_k&eud*ud^{k-1} \end{bmatrix}

注意边界数据 k=0 ans=1 = =

代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mdn 1000000007
using namespace std;

struct matrix{int a[3][3],n;};
inline void init(matrix &a)
{
	memset(a.a,0,sizeof(a.a));
}
matrix operator *(matrix a,matrix b)
{
	matrix tmp; init(tmp); tmp.n=a.n;
	//printf("%d\n",tmp.a[2][2]);
	for(int i=1;i<=a.n;i++)
	for(int j=1;j<=a.n;j++)
	for(int k=1;k<=a.n;k++)
	tmp.a[i][j] = ((ll)tmp.a[i][j] +(ll)a.a[i][k]*b.a[k][j]%mdn)%mdn;
	return tmp;
}

matrix ksm(matrix bs,ll mi)
{
	matrix tmp; tmp.n=bs.n; init(tmp);
	for(int i=1;i<=tmp.n;i++)	tmp.a[i][i]=1;
	while(mi)
	{
		if(mi&1)	tmp=tmp*bs;
		bs=bs*bs; mi>>=1;
	}
	return tmp;
}
char mp[1100][1100];
int ud,lr,eud,elr,cnt;
int ksm(int bs,ll mi)
{
	int ans=1;
	while(mi)
	{
		if(mi&1)	ans=(ll)ans*bs%mdn;
		bs=(ll)bs*bs%mdn; mi>>=1;
	}
	return ans;
}
int main()
{
	int h,w;ll k;
	scanf("%d%d%lld",&h,&w,&k);
	if(k<=1){printf("1\n");return 0;}
	for(int i=1;i<=h;i++)
	{
		scanf("%s",mp[i]+1);
		if(mp[i][1] == mp[i][w] && mp[i][1] =='#')	lr++;
		for(int j=2;j<=w;j++)	if(mp[i][j] == mp[i][j-1] && mp[i][j] == '#')	elr++;
	}
	for(int i=2;i<=h;i++)
		for(int j=1;j<=w;j++)	if(mp[i-1][j] == mp[i][j] && mp[i][j] == '#')	eud++;
	for(int i=1;i<=w;i++)	if(mp[1][i] == mp[h][i] && mp[h][i] == '#')	ud++;
	for(int i=1;i<=h;i++)	for(int j=1;j<=w;j++)	cnt+=(mp[i][j]=='#');
	if(lr && ud)
	{
		printf("1\n"); return 0;
	}
	if(!lr && !ud)
	{
		printf("%d\n",ksm(cnt,k-1)); return 0;
	}
	//printf("QAQ");
	if(lr)	swap(lr,ud),swap(elr,eud); // only ud
	matrix ans; ans.n = 2;// printf("%d %d\n",eud,ud);
	ans.a[1][1] = cnt; ans.a[1][2] = 0; ans.a[2][1] = 1; ans.a[2][2] = ud;
	ans = ksm(ans,k-2);
	int fin =(ll)ans.a[1][1]*eud%mdn + (ll)ud*eud%mdn*ans.a[2][1]%mdn;
	fin%=mdn; int mis = ksm(cnt,k-1);// printf("%d\n",mis);
	//printf("%d %d %d %d\n",ans.a[1][1],ans.a[1][2],ans.a[2][1],ans.a[2][2]);
	printf("%d\n",(mis-fin+mdn)%mdn);
	return 0;
}

 

posted @ 2018-12-28 20:53  寒雨微凝  阅读(143)  评论(0编辑  收藏  举报