比赛总结
这次div1打得还是很惨,2个小时里一直在wa A题,wa了七八次才ac,罚时太惨。。。
手速真的非常重要啊
A. Fight the Monster
题目链接
http://codeforces.com/contest/487/problem/A
题目大意
奥特曼打小怪兽,每局开始时,双方都会掉血,各自掉
max(0, ATKY − DEFM),max(0, ATKM − DEFY)
,初始时奥特曼血量是
HPY
,小怪兽血量是
HPM
。两个家伙谁先没血谁先输。但是奥特曼可以开挂,可以花
a
元钱让
思路
刚开始我很sb地去写了个二分,结果数据太硬,如果直接暴力枚举奥特曼的三个参数的大小的话,要么范围枚举小了会wa,要么范围枚举大了又会TLE
其实没必要二分,也没必要把奥特曼的三个参数都枚举,只需要枚举 DEFY 和 ATKY ,然后计算出最少所需的 HPY ,然后计算出这样的方案要花多少钱,更新答案就好了。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define INF 0x7fffffffffffffff
using namespace std;
typedef long long int LL;
LL HPy,ATKy,DEFy,HPm,ATKm,DEFm,h,a,d;
LL ans=INF;
inline void check()
{
for(LL aa=0;aa<=1000;aa++)
for(LL dd=0;dd<=1000;dd++)
{
LL hh;
ATKy+=aa;
DEFy+=dd;
LL lifey,lifem;
if(max((LL)0,ATKy-DEFm)==(LL)0)
{
ATKy-=aa;
DEFy-=dd;
continue;
}
else
{
lifem=HPm/max((LL)0,ATKy-DEFm);
if(HPm%max((LL)0,ATKy-DEFm)) lifem++;
}
if(max((LL)0,ATKm-DEFy)==(LL)0) hh=(LL)0;
else
{
lifey=1+lifem*max((LL)0,ATKm-DEFy);
if(HPy<lifey) hh=lifey-HPy;
else hh=0;
}
ATKy-=aa;
DEFy-=dd;
if(hh*h+aa*a+dd*d<ans) ans=hh*h+aa*a+dd*d;
}
}
int main()
{
scanf("%I64d%I64d%I64d%I64d%I64d%I64d%I64d%I64d%I64d",&HPy,&ATKy,&DEFy,&HPm,&ATKm,&DEFm,&h,&a,&d);
check();
printf("%I64d\n",ans);
return 0;
}
B. Strip
题目链接
http://codeforces.com/contest/487/problem/B
题目大意
给你一个序列
a
,要你将它分成连续的若干段,每一段长度大于等于
思路
膜用线段树干掉此题的各位大爷。。。
蒟蒻只会用set乱搞。。。
首先我们求出
然后我们再求出
f[i]=
前i个数字的最少划分次数。裸的做法是
O(n2)
的,但是我们这里可以再次用multiset将其优化到
O(nlogn)
,还是用到了
L[i]
的单调性。我们可以扫一遍序列
a
,从小到大枚举序列的下标
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>
#define MAXN 110000
#define INF 0x3f3f3f3f
using namespace std;
//int maxv[MAXN<<2],minv[MAXN<<2];
int a[MAXN],n;
int f[MAXN],L[MAXN]; //L[i]=右端点为i,左端点最远划分的位置
multiset<int>bst;
int main()
{
int l,s;
scanf("%d%d%d",&n,&s,&l);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int now=1;
for(int i=1;i<=n;i++)
{
while(!bst.empty()) //bst里保存的是[now,i-1]里的点
{
int minv=*bst.begin(),maxv=*(--bst.end()); //[now,i-1]里的最大值和最小值
minv=min(minv,a[i]),maxv=max(maxv,a[i]);
if(maxv-minv<=s) break;
bst.erase(bst.lower_bound(a[now]));
now++;
}
L[i]=now;
bst.insert(a[i]);
}
bst.clear(); //之后multiset里保存的是每个位置上的f值
now=0; //multiset里保存的是[now,i-1]的f值
for(int i=1;i<=n;i++)
{
if(i-l>=0) bst.insert(f[i-l]);
while(now<=i-l&&now<L[i]-1&&!bst.empty())
{
//cout<<now<<endl;
bst.erase(bst.lower_bound(f[now]));
now++;
}
if(bst.empty())
f[i]=INF;
else f[i]=*bst.begin()+1;
}
if(f[n]>=INF) printf("-1\n");
else printf("%d\n",f[n]);
return 0;
}
C. Prefix Product Sequence
题目链接
http://codeforces.com/contest/487/problem/C
题目大意
定义前缀和
pre[i]=∏ij=1a[j]modn
,要你构造序列
[1,2,...,n]
的一个排列序列
a
,使得
思路
显然若 n 为合数的话(4除外),无论怎么构造,得到的前缀和序列肯定是有重复元素的,这和hash的模数不能取合数的道理是一样的。而由于4比较特殊,它是正整数里的第一个合数,因此没有这个问题,我们可以手玩出n=4的解
其他情况下,显然
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 110000
using namespace std;
typedef long long int LL;
LL a[MAXN],rev[MAXN];
int n;
bool isPrime(int x)
{
for(int i=2;i*i<=x;i++)
if(!(x%i)) //!!!!!!
return false;
return true;
}
int main()
{
scanf("%d",&n);
if(n==4)
{
printf("YES\n1\n3\n2\n4\n");
return 0;
}
if(!isPrime(n))
{
printf("NO\n");
return 0;
}
printf("YES\n");
rev[1]=1;
for(int i=2;i<=n;i++)
rev[i]=rev[n%i]*rev[n%i]%n*i%n*(n/i)%n*(n/i)%n;
a[1]=1,a[n]=n;
for(int i=2;i<n;i++) a[i]=i*rev[i-1]%n;
for(int i=1;i<=n;i++) printf("%I64d\n",a[i]);
return 0;
}