• AtCoder abc165


    (注:上图真实,未F12)

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

    AtCoder比赛页面传送门

    A - We Love Golf

    AtCoder题目页面传送门

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

    (cin[1,1000],1leq aleq bleq1000)

    没什么可说的,直接跑一遍(bsim 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)円。

    (ninleft[101,10^{18} ight])

    直接暴力,一开始(100),每次( imes 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)),求在所有满足(1leq lis_1leq lis_2leqcdotsleq lis_nleq m)的长度为(n)的整数序列(lis)中,最大的(sumlimits_{i=1}^s[lis_{b_i}-lis_{a_i}=c_i]d_i)

    (nin[2,10],min[1,10],sin[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,cinmathbb Z),求(maxlimits_{i=1}^cleft{leftlfloordfrac{ai}b ight floor-aleftlfloordfrac ib ight floor ight})

    (ainleft[1,10^6 ight],b,cinleft[1,10^{12} ight])

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

    [egin{aligned}f(i)&=leftlfloordfrac{ai}b ight floor-aleftlfloordfrac ib ight floor\&=dfrac{ai}b-left{dfrac{ai}b ight}-aleft(dfrac ib-left{dfrac ib ight} ight)\&=dfrac{ai}b-left{dfrac{ai}b ight}-dfrac{ai}b+aleft{dfrac ib ight}\&=aleft{dfrac ib ight}-left{dfrac{ai}b ight}end{aligned} ]

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

    [egin{aligned}f(i)&=acdotdfrac ib-left{dfrac{ai}b ight}\&=dfrac{ai}b-left{dfrac{ai}b ight}\&=leftlfloordfrac{ai}b ight floorend{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+1leq n))场比赛,每场比赛描述为两个位置,表示每轮都是固定这两个位置的人打比赛,比赛的人集不相交。给出一组(m)场比赛的描述,使得每人在(n)轮里打的(2m)场比赛的对手两两不同。

    (ninleft[1,10^5 ight])

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

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

    (n)是偶数时呢?naive地想,跟(n)是奇数的方法一样的话,那位置关于中心对称的两个人会交两次手,不行。不难发现,根据(2m+1leq 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<<"
    ";
    	else{//n为偶数 
    		for(int i=1;i<=n/4&&m;i++,m--)cout<<i<<" "<<n/4*2-i+1<<"
    ";//左一半 
    		for(int i=n/4*2+1;m;i++,m--)cout<<i<<" "<<n/4*2+1+n-1-i<<"
    ";//右一半 
    	}
    	return 0;
    }
    

    F - LIS on Tree

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

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

    (ninleft[1,2 imes10^5 ight])

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

    考虑跟普通的线性结构上的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]<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    libnids-1.24 使用源码问题
    Linux学习man page
    shell 脚本,提取文件中的内容
    shell中的语法(1)
    python 爬取百度翻译进行中英互译
    matlab等高线绘制
    matlab 对tif数据高程图的处理分析
    python网络爬虫与信息提取 学习笔记day3
    python网络爬虫与信息提取 学习笔记day2
    python网络爬虫与信息提取 学习笔记day1
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/AtCoder-abc165.html
Copyright © 2020-2023  润新知