AGC003[BCDEF]题解
2018-12-28 有点累EF明天再写叭=v=
2018-12-29 update EF
B - Simplified mahjong
可以注意到 一段连续的非0序列都可以凑出
就是显然%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
注意边界数据 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;
}