Codeforces Round #707 (Div 2)

73 篇文章 0 订阅

链接:https://codeforces.com/contest/1501

A. Alexey and Train

看懂题目后直接模拟即可,我这里令 t 是每次出发的时间。

#include <bits/stdc++.h>
using namespace std;
#define N 105

long long n,a[N],b[N],tt[N];

void solve()
{
	long long tmp;
	scanf("%lld",&n);
	for (int i=1; i<=n; i++)
		scanf("%lld%lld",&a[i],&b[i]);
	for (int i=1; i<=n; i++)
		scanf("%lld",&tt[i]);
	
	long long t=0;
	for (int i=1; i<n; i++)
	{
		tmp=t+a[i]-b[i-1]+tt[i]+(b[i]-a[i]+1)/2;
		t=max(tmp,b[i]);
	}
	
	t+=a[n]-b[n-1]+tt[n];
	
	printf("%lld\n",t);
}

int main()
{
	int t;
	scanf("%d",&t);
	while (t--)
	{
		solve();
	}
	return 0;
}

B. Napoleon Cake

从后往前,用一个变量记录该处往下还可以浇多少个,每次维护一下就行了。

#include <bits/stdc++.h>
using namespace std;

int n,a[200005],f[200005];

void solve()
{
	scanf("%d",&n);
	for (int i=1; i<=n; i++)
	{
		f[i]=0;
		scanf("%d",&a[i]);
	}
	
	int cnt=0;
	for (int i=n; i>0; i--)
	{
		cnt=max(cnt,a[i]);
		f[i]= (cnt>0);
		cnt--;
	}
	
	for (int i=1; i<=n; i++)
		printf("%d ",f[i]);
	puts("");
}

int main()
{
	int t;
	scanf("%d",&t);
	while (t--)
	{
		solve();
	}
	return 0;
}

C. Going Home

这个技巧要记住, 四个数 abcd, a+d=b+c,即 a-b=c-d,那么由于 1≤ai≤2.5⋅1e6 ,任意两个数的差最多就 2.5*1e6 种, n 个数里面取两个数又有平方级别个取法,那么只要 n 平方级别大于 2.5e6 就行了,也就是说给的 200000 个数中只要取大概 5000 个数就行了。
(会发现,只有数少的时候才可能出现 NO 的情况,要是很多数的话(比如5000),肯定是有解的)
至于程序中的排序,我是想排完序相同的数会挤到一起,那选前5000个数可能答案出来快一点?

#include <bits/stdc++.h>
using namespace std;

int n;
int a[200005],c[200005];
vector<pair<int,int>> f[5000005];

bool cmp(int x, int y){return a[x]<a[y];}

#define I c[i]
#define J c[j]

void solve()
{
	scanf("%d",&n);
	for (int i=1; i<=n; i++)
	{
		scanf("%d",&a[i]);
		c[i]=i;
	}
		
	sort(c+1,c+n+1,cmp);
	
	for (int i=1,mi=min(n,5000); i<mi; i++)
	{
		for (int j=i+1; j<=mi; j++)
		{
			int sum=a[I]+a[J];
			if (!f[sum].empty())
			{
				for (auto A: f[sum])
				{
					if (A.first!=I && A.first!=J && A.second!=I && A.second!=J)
					{
						puts("YES");
						printf("%d %d %d %d\n",A.first, A.second, I,J);
						return;
					}
				}
			}
			f[sum].emplace_back(make_pair(I,J));
		}
	}
	
	puts("NO");
}

int main()
{
	solve();
	return 0;
}

D. Two chandeliers

因为 ai 互不相等,所以把不同颜色分开来考虑。
比如对于颜色 colr,a中在第 i 个, b 中在第 j 个,那么它们一次相遇点为 t ,则有
t= x1*n+i = x2*m+j,这里 x1 x2 都是整数,变形一下
x1*n-x2*m=j-i,就变成求不定方程的解了,扩展gcd来一下。
之后得出每种颜色第一次相遇的地方存到 s 数组里面(不会相遇的地方我用 -1 代替),每种颜色第一次相遇后,每过 lcm(n,m) 才会相遇一次。
之后我用了二分答案的方法,判断条件是对于时间 M ,要是不同次数大于等于 k ,那么就返回 1 ,否则返回 0。
这样就得到答案了,时间复杂度是 O(max(n,m)*lg(k)) 吧大概……

#include <bits/stdc++.h>
using namespace std;

long long n,m,k,nmgcd,nmlcm;

long long gcd(long long x, long long y){return y ? gcd(y,x%y) : x;}
long long lcm(long long x, long long y)
{
	long long tt=gcd(x,y);
	tt=x*y/tt;
	return tt;
}

long long exgcd(long long aa,long long bb,long long &x,long long &y)//扩展欧几里得算法
{
    if(bb==0)
    {
        x=1;y=0;
        return aa;  //到达递归边界开始向上一层返回
    }
    long long r=exgcd(bb,aa%bb,x,y);
    long long temp=y;    //把x y变成上一层的
    y=x-(aa/bb)*y;
    x=temp;
    return r;     //得到a b的最大公因数
}

#define N 500005

long long a[N],b[N],fa[N<<1],fb[N<<1],s[N<<1],MM;

bool check(long long x)
{
	long long cnt=x;
	for (long long i=MM; i; i--) if (s[i]>0)
	{
		if (x>=s[i])
		{
			cnt-=(x-s[i])/nmlcm+1;
		}
	}
	return cnt>=k;
}

void solve()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	MM=max(n,m)<<1;
	
	
	
	for (long long i=1; i<=n; i++)
	{
		scanf("%lld",&a[i]);
		fa[a[i]]=i;
	}
	
	for (long long i=1; i<=m; i++)
	{
		scanf("%lld",&b[i]);
		fb[b[i]]=i;
	}
	
	nmgcd=gcd(n,m);
	nmlcm=lcm(n,m);
	
	for (long long colr=MM; colr; colr--)
	{		
		if (fa[colr]==0 || fb[colr]==0 || (fb[colr]-fa[colr])%nmgcd!=0)
		{
			s[colr]=-1;
			continue;
		}
		
		if (fa[colr]==fb[colr])
		{
			s[colr]=fa[colr];
			continue;
		}
		
		long long x1,x2,tmp;
		exgcd(n,m,x1,x2);
		x1*=((fb[colr]-fa[colr])/nmgcd);
		x2*=((fb[colr]-fa[colr])/nmgcd);
		tmp=x1*n+fa[colr];
		tmp=((tmp-1)%nmlcm+nmlcm)%nmlcm+1;
		s[colr]=tmp;
	}
	
	long long L=1,R=(1ll<<61),M,ans;
	for (; L<R; )
	{
		M=(L+R)>>1;
		if (check(M))
			ans=R=M;
		else
			L=M+1;
	}
	
	printf("%lld\n",ans);
}

int main()
{
	solve();
	return 0;
}

/*
3 8 41
1 3 2
1 6 4 3 5 7 2 8
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值