这次div1打得还是很惨,2个小时里一直在wa A题,wa了七八次才ac,罚时太惨。。。
手速真的非常重要啊
http://codeforces.com/contest/487/problem/A
奥特曼打小怪兽,每局开始时,双方都会掉血,各自掉 max(0, ATKY − DEFM),max(0, ATKM − DEFY) ,初始时奥特曼血量是 HPY ,小怪兽血量是 HPM 。两个家伙谁先没血谁先输。但是奥特曼可以开挂,可以花 a 元钱让 ATKY 加1,花 d 元钱让 DEFY 加1,花 h 元钱让 HPY 加1,问最少花多少钱,他才能打败小怪兽
刚开始我很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;
}
http://codeforces.com/contest/487/problem/B
给你一个序列 a ,要你将它分成连续的若干段,每一段长度大于等于 l ,最大值减最小值小于等于 s 。问最少分成多少段。
膜用线段树干掉此题的各位大爷。。。
蒟蒻只会用set乱搞。。。
首先我们求出 L[i]= 分出的一段子序列,右端点为 i ,左端点最远的位置。显然 L[i] 具有单调性,即 i<j,L[i]≤L[j] ,这个大家自己脑补下就能明白。于是我们可以扫一遍整个序列,从小到大枚举序列的下标 i ,维护一个multiset,其中的所有元素的最大值减最小值小于等于 s ,并维护一个变量 now ,代表这个multiset里维护的元素是属于区间 [now,i−1] 的,如果 a[i] 加入后,集合里最大值减最小值大于 s ,则需要在multiset里删除掉a[now] a[now] ,如此反复直到最大值减最小值小于等于 s 为止,此时 L[i]=now
然后我们再求出 f[i]= 前i个数字的最少划分次数。裸的做法是 O(n2) 的,但是我们这里可以再次用multiset将其优化到 O(nlogn) ,还是用到了 L[i] 的单调性。我们可以扫一遍序列 a ,从小到大枚举序列的下标 i ,在集合里维护 [L[i],i−l] 段的 f[] 值,每次从中取最小值做DP即可。若取不出一个 f 值的话,就赋 f[i]=inf ,表明这个状态无效了
#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;
}
http://codeforces.com/contest/487/problem/C
定义前缀和 pre[i]=∏ij=1a[j]modn ,要你构造序列 [1,2,...,n] 的一个排列序列 a ,使得 a 的前缀和组成的序列是 [0,1,2,...,n−1] 的一个排列
显然若 n 为合数的话(4除外),无论怎么构造,得到的前缀和序列肯定是有重复元素的,这和hash的模数不能取合数的道理是一样的。而由于4比较特殊,它是正整数里的第一个合数,因此没有这个问题,我们可以手玩出n=4的解
其他情况下,显然 a[1]=1,a[n]=n ,剩余的部分,我们这样构造: a[i]=irev[i−1]modn ,这样的话,就能在mod n的意义下使第 i 项前缀积避免受到第 i−1 及以前的元素的影响,保证每一项前缀积不一样
#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;
}