AtCoder Grand Contest 015

Preface

STO CXR ORZ爆切所有题,同时成功完成了所有题目一遍A的record(感谢陈指导试水)

不过qs这场对比起最近的几场难度还是比较低的,但F感觉还是因为陈指导的神仙气质才能在迷雾中照亮前路


A - A+...+B Problem

强制\(A,B\)两个数都要选,那么剩下的数能构成的范围就是\([(n-2)\times A,(n-2)\times B]\)

注意特判\(n=1\)\(A>B\)的情况

#include<cstdio>
#define RI register int
#define CI const int&
int n,a,b;
int main()
{
	scanf("%d%d%d",&n,&a,&b); if (a>b) return puts("0"),0;
	if (n==1) return puts(a==b?"1":"0"),0;
	return printf("%lld\n",1LL*(n-2)*b-1LL*(n-2)*a+1),0;
}

B - Evilator

因为\(S_1\)一定是U\(S_n\)一定是D,因此对于某个位置,只要根据它的按钮就可以确定它哪个方向是一步到位,另一个方向是两步的

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
const int N=100005;
int n; char s[N]; long long ans;
int main()
{
	scanf("%s",s+1); n=strlen(s+1); for (RI i=1;i<=n;++i)
	if (s[i]=='U') ans+=2*(i-1)+n-i; else ans+=i-1+2*(n-i);
	return printf("%lld",ans),0;
}

C - Nuske vs Phantom Thnook

经典老题,直接拷之前写的代码了

众所周知无环的联通块个数=点数-边数,分别用二维前缀和维护这两个东西即可

复杂度\(O(nm+q)\)

#include<cstdio>
#include<cctype>
#define RI register int
#define CI const int&
using namespace std;
const int N=2005;
int n,m,q,a[N][N],pt[N][N],eg[N][N],h[N][N],l[N][N],x1,x2,y1,y2; char ch;
inline void get_digit(char& ch)
{
	while (!isdigit(ch=getchar()));
}
inline int getpt(CI x1,CI y1,CI x2,CI y2)
{
	return pt[x2][y2]-pt[x1-1][y2]-pt[x2][y1-1]+pt[x1-1][y1-1];
}
inline int geth(CI x,CI y1,CI y2)
{
	return h[x][y2]-h[x][y1-1];
}
inline int getl(CI y,CI x1,CI x2)
{
	return l[x2][y]-l[x1-1][y];
}
inline int geteg(CI x1,CI y1,CI x2,CI y2)
{
	return eg[x2][y2]-eg[x1-1][y2]-eg[x2][y1-1]+eg[x1-1][y1-1]-geth(x1-1,y1,y2)-getl(y1-1,x1,x2);
}
int main()
{
	RI i,j; for (scanf("%d%d%d",&n,&m,&q),i=1;i<=n;++i)
	for (j=1;j<=m;++j) get_digit(ch),a[i][j]=ch&15;
	for (i=1;i<=n;++i) for (j=1;j<=m;++j)
	pt[i][j]=pt[i][j-1]+pt[i-1][j]-pt[i-1][j-1]+a[i][j];
	for (i=1;i<n;++i) for (j=1;j<=m;++j)
	h[i][j]=h[i][j-1]+(a[i][j]==1&&(a[i][j]==a[i+1][j]));
	for (j=1;j<m;++j) for (i=1;i<=n;++i)
	l[i][j]=l[i-1][j]+(a[i][j]==1&&(a[i][j]==a[i][j+1]));
	for (i=1;i<=n;++i) for (j=1;j<=m;++j)
	{
		eg[i][j]=eg[i-1][j]+eg[i][j-1]-eg[i-1][j-1];
		if (a[i][j]) eg[i][j]+=(i!=1?(a[i][j]==a[i-1][j]):0)+(j!=1?(a[i][j]==a[i][j-1]):0);
	}
	for (i=1;i<=q;++i)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		printf("%d\n",getpt(x1,y1,x2,y2)-geteg(x1,y1,x2,y2));
	}
	return 0;
}

D - A or...or B Problem

首先我们发现\(A,B\)二进制下相同的那一段前缀是无用的,显然可以删去

接下来我们设最高的不同位为\(i\),容易发现此时一定是\(B\)这一位上为\(1\)\(A\)这一位上为\(1\)

此时我们若

第i位为0

这种方案就相当于\([2^i,B]\)中的数都不能选,答案就是在\([A,2^i)\)中选择的方案数

显然\([A,2^i)\)中的每一个数都可以作为答案,而且显然做或操作也不能让这个值超出\(2^i\)的范围,因此这部分的答案就是\(2^i-A\)

第i位为1

其实强制\(B\)的第\(i\)位必须选,我们把这一位拿掉就相当于后面的在\([A,2^i)\)\([0,B]\)中选择

找到\(B\)中此时最高位的\(1\)所在的位置,此时显然\(2^0,2^1\cdots,2^{j}\)都在\([0,B]\)中,因此我们可以构造出\([0,2^{j+1})\)的数

因此现在的问题就变为了在\([0,2^{j+1})\)\([A,2^i)\)中任选,我们再讨论一下:

  • \(A\le 2^{j+1}\),此时两个区间的并就达到了答案的上界\([0,2^i)\)了,因此此时答案就是\(2^i\)
  • \(A>2^{j+1}\),还是利用或的性质,只会使一个数变大但无法超出这个区间的限制,因此此时答案就是\(2^i-A+2^{j+1}\)

因此这道题就做完了,复杂度\(O(\log B)\)

#include<cstdio>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
int a,b,n;
signed main()
{
	RI i,j; scanf("%lld%lld",&a,&b); if (a==b) return puts("1"),0;
	for (i=60;~i;--i) if (((a>>i)&1)!=((b>>i)&1)) break;
	a&=(1LL<<i)-1; b&=(1LL<<i)-1; int tp=(1LL<<i)-a; 
	for (j=i-1;~j;--j) if ((b>>j)&1) break;
	if (a<=(1LL<<j+1)) printf("%lld",(1LL<<i)+tp);
	else printf("%lld",2LL*tp+(1LL<<j+1)); return 0;
}

E - Mr.Aoki Incubator

首先我们可以把每个人看作一条射线,斜率就是速度,截距就是初始位置,它们与\(y\)轴的交点就是射线的端点

粗略地一想我们以为一条射线\(y=k_ix+b_i\)\(y=k_jx+b_j\)能染色到对方的条件是它们在一四象限有交点,但是后来发现可以间接地染色

我们分析后发现对于某条射线\(y=k_jx+b_j\)能染到\(y=k_ix+b_i\)的条件仅仅是\(k_j>k_i,b_j<b_i\)\(k_j<k_i,b_j>b_i\),此时\(j\)还可以把它和\(i\)之间的其它直线都染了

容易发现如果我们把所有射线按\(b_i\)排序后,我们对于每条直线\(i\)找出\(l_i=\min _{k_j>k_i} b_j\)\(r_i=\max _{k_j<k_i} b_j\),那么\([l_i,r_i]\)中的任意一条直线被染色都可以把\(i\)染色

考虑如何求出\([l_i,r_i]\),显然我们可以在扫描的时候维护一个元素递增的栈,在上面二分即可

那么现在问题就转化为有\(n\)个区间,现在要选择某些点使得每个区间中都要有至少一个点

\(f_i\)表示上一个以\(i\)为结尾的方案数,考虑\(f_i\)能转移到\(f_j\)的充要条件是\((i,j)\)中不存在一个完整的区间

因此我们考虑对于某个\(i\)它能转移到的最远点(不能取)就是所有左端点大于它的区间中右端点的最小值,显然我们可以把所有区间按\(l_i\)排序后找出这个范围

然后就可以直接DP了,这里由于区间修改单点查询且有严格的前后顺序因此可以直接差分处理,总复杂度\(O(n\log n)\)

#include<cstdio>
#include<algorithm>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod=1e9+7;
struct line
{
	int k,b;
	friend inline bool operator < (const line& A,const line& B)
	{
		return A.b<B.b;
	}
}l[N];
struct interval
{
	int l,r;
	friend inline bool operator < (const interval& A,const interval& B)
	{
		return A.l<B.l;
	}
}a[N]; int n,stk[N],top,f[N],cr[N],cur,mr;
inline void inc(int& x,CI y) { if ((x+=y)>=mod) x-=mod; }
inline void dec(int& x,CI y) { if ((x-=y)<0) x+=mod; }
inline int find1(CI x)
{
	int L=1,R=top,mid,ret; while (L<=R)
	if (l[stk[mid=L+R>>1]].k>=x) ret=mid,R=mid-1; else L=mid+1; return ret;
}
inline int find2(CI x)
{
	int L=1,R=top,mid,ret; while (L<=R)
	if (l[stk[mid=L+R>>1]].k<=x) ret=mid,R=mid-1; else L=mid+1; return ret;
}
int main()
{
	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&l[i].b,&l[i].k);
	for (sort(l+1,l+n+1),i=1;i<=n;++i)
	{
		if (!top||l[stk[top]].k<l[i].k) stk[++top]=i; a[i].l=stk[find1(l[i].k)];
	}
	for (top=0,i=n;i;--i)
	{
		if (!top||l[stk[top]].k>l[i].k) stk[++top]=i; a[i].r=stk[find2(l[i].k)];
	}
	for (sort(a+1,a+n+1),mr=n+1,i=j=n;~i;--i)
	{
		while (j&&a[j].l==i+1) mr=min(mr,a[j--].r); cr[i]=mr;
	}
	for (f[0]=1,f[1]=mod-1,i=0;i<=n;++i)
	inc(cur,f[i]),inc(f[i+1],cur),dec(f[cr[i]+1],cur);
	return inc(cur,f[n+1]),printf("%d",cur),0;
}

F - Kenus the Ancient Greek

第一问的处理

首先我们考虑到如果\(A=B\)的话那么我们可以利用经典结论,消耗辗转相除次数最多的情况就是一对相邻的斐波那契数

稍加推广我们发现这个结论对于\(A<B\)的情况仍然成立,具体地,我们找到\(\le A\)的最大的\(Fib_i\),若\(Fib_{i+1}\le B\)那么\(i\)就是答案,否则答案就是\(i-1\)

第二问的处理

我们考虑统计个数,非常直观地我们设想到相同步数的不同构造肯定可以通过在某些步数时修改下递推系数,即\(Fib_i=Fib_{i-2}+k\times Fib_{i-1}\)

感性地,我们感觉出每次变化的系数不能很大,并且要修改的位置不是很多,接下来我们来证明两个结论

  • \(k\le 2\):由于\(Fib_i\le 2Fib_{i-1}\),若\(k>2\)则某个值必然至少翻倍。而此时最后的值也必然至少翻倍,必然超过了上界\(A\)。但是注意要去除最后一步的情况,因为此时若\(A,B\)相差悬殊可以导致\(k\)取到很大
  • 要修改的位置至多为一个(除了最后一步):考虑\(\lim_{i\to \infty}\frac{Fib_{i+1}}{Fib_i}=\frac{\sqrt 5+1}{2}\),我们手动打个表发现当\(n=10\)左右这个值就在\(1.618\)左右了。我们考虑我们每次将一个\(k\)改为\(2\)就会导致\(Fib_i\)乘上\(\frac{\sqrt 5+1}{2}\),而\((\frac{\sqrt 5+1}{2})^2>2\),因此修改的地方不可能超过一处

因此我们就可以直接枚举在哪个位置修改\(k\)来算答案了,考虑快速算出修改后的值可以通过记录系数的方法来处理

由于\(10^{18}\)以内的斐波那契数大约有\(88\)个,因此总复杂度即为\(O(88\times Q)\)

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
const int N=88,mod=1e9+7;
int q,t,A,B,fib[N],f[N][2];
#define calc(a,b) (B>=b?((B-b)/a+1)%mod:0)
inline int solve(CI A,CI B)
{
	int ret=calc(fib[t],fib[t+1]); for (RI i=1;i<t;++i)
	{
		int a=f[t-i][0]*fib[i]+f[t-i][1]*(fib[i-1]+2LL*fib[i]),
		b=f[t-i+1][0]*fib[i]+f[t-i+1][1]*(fib[i-1]+2LL*fib[i]);
		if (a<=A&&b<=B) (ret+=calc(a,b))%=mod;
	}
	return ret;
}
signed main()
{
	RI i; for (fib[0]=fib[1]=f[0][0]=f[1][1]=1,i=2;i<N;++i)
	fib[i]=fib[i-1]+fib[i-2],f[i][0]=f[i-1][0]+f[i-2][0],f[i][1]=f[i-1][1]+f[i-2][1];
	for (scanf("%lld",&q),i=1;i<=q;++i)
	{
		scanf("%lld%lld",&A,&B); if (A>B) swap(A,B);
		if (A==1) { printf("1 %lld\n",B%mod); continue; }
		if (B<=2) { printf("1 %lld\n",A*B%mod); continue; }
		t=upper_bound(fib+1,fib+N,A)-fib-1; if (fib[t+1]>B) --t;
		printf("%lld %lld\n",t,(solve(A,B)+solve(A,A))%mod);
	}
	return 0;
}

Postscript

所以说陈指导时我们的红太阳

posted @ 2020-12-02 08:50  空気力学の詩  阅读(123)  评论(0编辑  收藏  举报