AtCoder abc165 - AtCoder Beginner Contest 165

(注:上图真实,未F12)

第一次AK ABC,写个题解纪念一下吧

AtCoder比赛页面传送门

A - We Love Golf

AtCoder题目页面传送门

给定\(a,b,c\),问区间\([b,c]\)内是否存在整数\(x\)使得\(a\mid x\)

\(c\in[1,1000],1\leq a\leq b\leq1000\)

没什么可说的,直接跑一遍\(b\sim c\)判断一下即可。

代码(现场代码,下同):

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a,b,c;
	cin>>a>>b>>c;
	for(int i=b;i<=c;i++)if(i%a==0)return puts("OK"),0;
	puts("NG");
	return 0;
}

B - 1%

AtCoder题目页面传送门

原有\(100\)円,每年增加\(1\%\),不足\(1\)円的零头被抛弃。问多少年后能\(\geq n\)円。

\(n\in\left[101,10^{18}\right]\)

直接暴力,一开始\(100\),每次\(\times 1.01\)并下取整直到\(\geq n\),顺便计个次。

观察样例可发现,最多增加\(3760\)次,不用担心TLE。

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long n;
	cin>>n;
	long long now=100;
	for(int i=1;;i++){
		now=1.01*now;
		if(now>=n)return cout<<i,0;
	}
	return 0;
}

C - Many Requirements

洛谷题目页面传送门 & AtCoder题目页面传送门

给定\(n,m,s\)\(s\)个四元组,第\(i\)个为\((a_i,b_i,c_i,d_i)\),求在所有满足\(1\leq lis_1\leq lis_2\leq\cdots\leq lis_n\leq m\)的长度为\(n\)的整数序列\(lis\)中,最大的\(\sum\limits_{i=1}^s[lis_{b_i}-lis_{a_i}=c_i]d_i\)

\(n\in[2,10],m\in[1,10],s\in[1,50]\)

考虑枚举所有的\(lis\)算答案并取最大值。看起来满足条件的\(lis\)的数量大概是\(\mathrm O(m^n)\)级别的,但其实绝大多数每个数\(\in[1,m]\)、长度为\(n\)的整数序列都不满足条件。考虑算出满足条件的\(lis\)的数量最大是多少。

显然,当\(n,m\)都最大时,数量最大。于是我们写这样一个程序跑一跑:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,l) for(int i=l;i<=10;i++)
int main(){
	int ans=0;
	rep(a,1)rep(b,a)rep(c,b)rep(d,c)rep(e,d)rep(f,e)rep(g,f)rep(h,g)rep(i,h)rep(j,i)ans++;
	cout<<ans;
	return 0;
}

跑出来当\(n=m=10\)时数量只有\(92378\)。这时候就可以放心大胆地暴搜了,对于一个\(lis\)计算答案的时间复杂度为\(\mathrm O(s)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=10,S=50;
int n,m,s;
int a[S+1],b[S+1],c[S+1],d[S+1];
int lis[N+1];
int ans;
void dfs(int x=1,int now=1){
	if(x==n+1){
		int res=0;
		for(int i=1;i<=s;i++)if(lis[b[i]]-lis[a[i]]==c[i])res+=d[i];
		ans=max(ans,res);
		return;
	}
	lis[x]=now;
	for(int i=now;i<=m;i++)dfs(x+1,i);
}
int main(){
	cin>>n>>m>>s;
	for(int i=1;i<=s;i++)cin>>a[i]>>b[i]>>c[i]>>d[i];
	dfs();
	cout<<ans;
	return 0;
}

D - Floor Function

洛谷题目页面传送门 & AtCoder题目页面传送门

给定\(a,b,c\in\mathbb Z\),求\(\max\limits_{i=1}^c\left\{\left\lfloor\dfrac{ai}b\right\rfloor-a\left\lfloor\dfrac ib\right\rfloor\right\}\)

\(a\in\left[1,10^6\right],b,c\in\left[1,10^{12}\right]\)

直接枚举\(i\)\(\mathrm O(c)\)的,显然过不去。考虑对这个\(\left\lfloor\dfrac{ai}b\right\rfloor-a\left\lfloor\dfrac ib\right\rfloor\)式子变变形。看上去没有思路,考虑将下取整换为原数减去小数部分的形式:

\[\begin{aligned}f(i)&=\left\lfloor\dfrac{ai}b\right\rfloor-a\left\lfloor\dfrac ib\right\rfloor\\&=\dfrac{ai}b-\left\{\dfrac{ai}b\right\}-a\left(\dfrac ib-\left\{\dfrac ib\right\}\right)\\&=\dfrac{ai}b-\left\{\dfrac{ai}b\right\}-\dfrac{ai}b+a\left\{\dfrac ib\right\}\\&=a\left\{\dfrac ib\right\}-\left\{\dfrac{ai}b\right\}\end{aligned} \]

此时显然函数值只与\(i\bmod b\)有关,即\(f(i)=f(i\bmod b)\)。不妨只考虑所有的\(i<b\)。此时显然\(\left\{\dfrac ib\right\}=\dfrac ib\)。所以

\[\begin{aligned}f(i)&=a\cdot\dfrac ib-\left\{\dfrac{ai}b\right\}\\&=\dfrac{ai}b-\left\{\dfrac{ai}b\right\}\\&=\left\lfloor\dfrac{ai}b\right\rfloor\end{aligned} \]

显然当\(i=\min(b-1,c)\)时,\(f(i)\)最大。

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long a,b,c;
	cin>>a>>b>>c;
	if(b-1>c)cout<<int(a*(1.*c/b));
	else cout<<int(a*(1.*(b-1)/b));
	return 0;
}

E - Rotation Matching

这其实是最难的一题,因为F是套路题。

洛谷题目页面传送门 & AtCoder题目页面传送门

\(n\)个人,共\(n\)轮比赛,每轮所有人往右移一格,最后面的人移到最前面。每轮有\(m(2m+1\leq n)\)场比赛,每场比赛描述为两个位置,表示每轮都是固定这两个位置的人打比赛,比赛的人集不相交。给出一组\(m\)场比赛的描述,使得每人在\(n\)轮里打的\(2m\)场比赛的对手两两不同。

\(n\in\left[1,10^5\right]\)

一个显然的结论是:若一个人满足题意,则所有人满足题意,因为所有人是轮换对称的(是这么说的吧?)。

\(n\)是奇数时,方法显然:位置\(i\)\(n-i+1\)配。

\(n\)是偶数时呢?naive地想,跟\(n\)是奇数的方法一样的话,那位置关于中心对称的两个人会交两次手,不行。不难发现,根据\(2m+1\leq n\)可以得出至少有\(2\)个位置没有比赛,暗示了我们可以牺牲空间换取奇偶性。然后这也没啥可说的,只能手玩一会儿,可以玩出来一种方法:从中间截半,如果两半大小是奇数,就把左一半的最右边分到右一半去。此时两半大小都是偶数,左边那半按naive方法,右边那半牺牲掉最后一个,剩下来奇数个,用奇数的方法。这样是正确的,别问我咋想到的。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
	cin>>n>>m;
	if(n&1)//n为奇数 
		for(int i=1;i<=m;i++)cout<<i<<" "<<n-i+1<<"\n";
	else{//n为偶数 
		for(int i=1;i<=n/4&&m;i++,m--)cout<<i<<" "<<n/4*2-i+1<<"\n";//左一半 
		for(int i=n/4*2+1;m;i++,m--)cout<<i<<" "<<n/4*2+1+n-1-i<<"\n";//右一半 
	}
	return 0;
}

F - LIS on Tree

洛谷题目页面传送门 & AtCoder题目页面传送门

给定一棵大小为\(n\)的树,求对于每个点,\(1\)到这个点组成的序列的LIS。

\(n\in\left[1,2\times10^5\right]\)

(这是我第一个做出来的题)

考虑跟普通的线性结构上的LIS设类似的状态,状态转移方程也类似。

普通的线性LIS可以用BIT优化,而这里树上DFS回溯的时候需要撤销,我们可以对于每个离散化后的值开一个multiset来撤销,每次将multiset中最大的数扔到BIT里。可是,这样有可能变小,BIT是处理不了的,于是可以用线段树维护。

代码:

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=200000;
int n;
int a[N+1];
vector<int> nums;
void discrete(){//离散化 
	sort(nums.begin(),nums.end());
	nums.resize(unique(nums.begin(),nums.end())-nums.begin());
	for(int i=1;i<=n;i++)a[i]=lower_bound(nums.begin(),nums.end(),a[i])-nums.begin();
}
vector<int> nei[N+1];
multiset<int> st[N];
struct segtree{//线段树 
	struct node{int l,r,mx;}nd[N<<2];
	#define l(p) nd[p].l
	#define r(p) nd[p].r
	#define mx(p) nd[p].mx
	void bld(int l=0,int r=nums.size()-1,int p=1){
		l(p)=l;r(p)=r;mx(p)=0;
		if(l==r)return;
		int mid=l+r>>1;
		bld(l,mid,p<<1);bld(mid+1,r,p<<1|1);
	}
	void init(){bld();}
	void sprup(int p){mx(p)=max(mx(p<<1),mx(p<<1|1));}
	void chg(int x,int v,int p=1){//单点修改 
		if(l(p)==r(p))return mx(p)=v,void();
		int mid=l(p)+r(p)>>1;
		chg(x,v,p<<1|(x>mid));
		sprup(p);
	}
	int _mx(int l,int r,int p=1){//区间最大值 
		if(l>r)return 0;
		if(l<=l(p)&&r>=r(p))return mx(p);
		int mid=l(p)+r(p)>>1,res=0;
		if(l<=mid)res=max(res,_mx(l,r,p<<1));
		if(r>mid)res=max(res,_mx(l,r,p<<1|1));
		return res;
	}
}segt;
int dp[N+1],ans[N+1];
void dfs(int x=1,int fa=0){ 
	dp[x]=segt._mx(0,a[x]-1)+1;//转移方程 
	ans[x]=max(ans[fa],dp[x]);
	st[a[x]].insert(dp[x]);
	segt.chg(a[x],*--st[a[x]].end());
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa)continue;
		dfs(y,x);
	}
	st[a[x]].erase(st[a[x]].find(dp[x]));//回溯时撤销 
	segt.chg(a[x],*--st[a[x]].end());//撤销 
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i],nums.pb(a[i]);
	discrete();
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		nei[x].pb(y);nei[y].pb(x);
	}
	for(int i=0;i<nums.size();i++)st[i].insert(0);
	segt.init();
	dfs();
	for(int i=1;i<=n;i++)cout<<ans[i]<<"\n";
	return 0;
}
posted @ 2020-05-02 21:46  ycx060617  阅读(378)  评论(0编辑  收藏  举报