……

AtCoder ABC 163 题解(难题日常咕咕咕

第一场 AtCoder 比赛,结果你告诉我是 unrated?
看来只能做人均会的题了。
总分:\(100+200+300+400+0(AC×5)+0=1000\)
写一下简要题解:
按我 \(A\) 题顺序来。
(网太卡了,把题全打开,谁先加载出来先搞谁

\(C\).

给一棵以 \(1\) 为根的树,并给出其它点的父亲,求每个点有几个儿子。
直接开个数组记录就好了:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;

#define read(x) scanf("%d",&x)
#define readl(x) scanf("%lld",&x)
#define ll long long 
#define ull unsigned long long

int n,l;
int b[200005]={0};

int main()
{
	read(n);
	for(int i=2;i<=n;i++) read(l),b[l]++;
	for(int i=1;i<=n;i++) printf("%d\n",b[i]);
	return 0;
} 

然后 \(CE\) 只因选错语言,还 \(CE\) 了两次,罚时 \(10\;min\) 亏死了。

\(A\).

给出圆的半径,求圆的直径。

\[C=2\pi r \]

依稀记得 \(FFT\)\(\pi=acos(-1)\),直接算就行了。
不卡精度真是太好了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;

#define read(x) scanf("%d",&x)
#define readl(x) scanf("%lld",&x)
#define ll long long 
#define ull unsigned long long

double pi=acos(-1.0);
double n; 

int main()
{
	cin>>n;
	printf("%.20lf\n",2*n*pi); 
	return 0;
} 

\(B\).

有一些工作需要的时间和拥有来办公的总时间,求做完工作后剩多少时间,若做不完,输出 \(-1\)
前缀和,注意long long

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;

#define read(x) scanf("%d",&x)
#define readl(x) scanf("%lld",&x)
#define ll long long 
#define ull unsigned long long

int n,m;
int b[10005];
ll sum=0;

int main()
{
	read(n),read(m);
	for(int i=1;i<=m;i++) read(b[i]),sum+=b[i];
	if(sum>n) printf("%d\n",-1);
	else printf("%d\n",n-sum);
	return 0;
} 

\(D\)

\(10^{100},10^{100}+1......10^{100}+N\) 中选 \(k\)\(N+1\) 个数,求可以得到值数的和。
稍有思维难度,但还是一眼题。
发现得到的数都是连续的,直接求最大值和最小值相减即可,选不同个的最值显然是可以前、后缀和优化递推的。
复杂度 \(\mathcal O(n)\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;

#define read(x) scanf("%d",&x)
#define readl(x) scanf("%lld",&x)
#define ll long long 
#define ull unsigned long long
#define MOD 1000000007

int n,m;
ll q[200005],h[200005];
ll sum=0;

int main()
{
	read(n),read(m);
	q[0]=0,h[n+1]=0;
	for(int i=1;i<=n;i++) q[i]=q[i-1]+1ll*i;
	for(int i=n;i>=1;i--) h[i]=h[i+1]+1ll*i;
	for(int i=m;i<=n;i++) sum=(sum+h[n-i+1]-q[i-1]+1)%MOD;
	printf("%lld\n",(sum+1)%MOD);
	return 0;
} 

\(E\).

\(n\) 个人,每个人有一个价值 \(c_i\) ,把他们打乱,假设从前的位置是 \(a_i\),现在的位置是 \(b_i\),最大化 \(\sum\limits_{i=1}^nc_i|a_i-b_i|\)
贪心,优先放置 \(c\) 小的人。
为什么呢?

由于我们在记录状态时无法大范围看全局,先选择影响小的人。
我们设 \(dp_{i,j}\) 为选 \(j-i+1\)个人放在 \(i\)\(j\) 的区间上的最大贡献。
我们以此为基础扩大区间,即判断先一个应该放左边还是右边。

这里转移方程易得为:

\[dp_{i,j}=\max\{\max(dp_{i+1,j}+c_{j-i+1}|a_{j-i+1}-l|,dp_{i,j-1}+c_{j-i+1}|a_{j-i+1}-r|)\} \]

\(Code\):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

#define ll long long 
#define read(x) scanf("%d",&x)

struct node
{
	int id,val;
}a[2005];
ll dp[2005][2005];
int n;

bool cmp(node n,node m){return n.val<m.val;}

int main()
{
	read(n);
	for(int i=1;i<=n;i++) read(a[i].val),a[i].id=i;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++) dp[i][i]=1ll*a[1].val*abs(a[1].id-i);
	for(int i=2;i<=n;i++)
	{
		for(int l=1;l+i-1<=n;l++)
		{
			int r=l+i-1;
			dp[l][r]=0;
			dp[l][r]=max(dp[l][r],max(dp[l+1][r]+1ll*a[i].val*abs(a[i].id-l),dp[l][r-1]+1ll*a[i].val*abs(a[i].id-r)));
		}
	}
	printf("%lld\n",dp[1][n]);
}

\(F\).

咕咕咕......
最后得了 \(1000\;pts+39.10\;min\;\;\;rk1654\)
太菜了,没人权, \(dp\) 功夫需要继续加强。
要是没有选错语言就能 \(rk1081\) 了。(然而没有切出 \(E\) 题才是原罪。

posted @ 2020-04-20 23:09  童话镇里的星河  阅读(446)  评论(0编辑  收藏  举报