这次离线赛考的是NOIP2011,考得比较差,其实试卷比较水,水出新高度了。但是就考了160分,还是因为大意了,说实话,我一直在想第二题那个Sigma 是怎么计算的,很虚。虽然最后证明我的想法是正确的,但是由于这道题花的时间太少了,导致我WA了。就30分……
第三题玄学贪心水了30分,还是比较好的,就是第二题可惜了。
这道题是道水题,纯属送分,只要把杨辉三角计算出来,再使用快速幂,处理出系数就可以了。不多解释。
附上AC代码:
#include
#include
#include
#include
#define M 1050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int P=10007;
const int S=20;
//ÎļþÃû Êä³öµ÷ÊÔ cstdio long long 1LL
int a,b,k,m,n;
int dp[M][M];//¼ÆËãÑî»ÔÈý½Ç
struct water {
void solve() {
dp[1][1]=1;
FOR(i,2,k+1) {
FOR(j,1,i) {
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%P;
}
}
cout<1][m+1]<struct _pAC {
int fast(int a,int b) {
int res=1;
int x=a;
while(b) {
if(b&1) {
res=(res*x)%P;
}
b/=2;
x=(x*x)%P;
}
return res;
}
void solve() {
dp[1][1]=1;
if(!(a||b||k||n||m)) {
puts("0");
exit(0);
}
FOR(i,2,k+1) {
FOR(j,1,i) {
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%P;
}
}
long long ans;
ans=(1LL*(fast(a,n)*fast(b,m))%P*dp[k+1][m+1])%P;
cout<int main() {
cin>>a>>b>>k>>n>>m;
pAC.solve();
return 0;
}
这道题显然可以用二分法来写,要注意的是S属于[1,1e12],因此要用long long 来存储。除此之外就是对W进行二分查找了。
至于为什么用二分法,我们不难看出,当W变小时,满足条件的j会变多,Y自然会变大,因此Y与W的关系是单调的。
之后就是对这个Sigma的处理了。
这个问题也是比较好处理的。我们只需要用前缀和优化一下就好了。
先预处理出1到i区间中的满足条件的j的数量cnt[i],同时记录满足条件的价值和val[i]。
对于每一个区间[L,R]只需要计算出(cnt[B[i].R]-cnt[B[i].L-1])*(val[B[i].R]-val[B[i].L-1])即可。
附上AC代码:
#include
#include
#include
#include
#define ll long long
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int n,m;
ll stan;
struct Stone {
int value,weight;
} A[M];
struct node {
int L,R;
} B[M];
ll cnt[M];
ll val[M];
long long res;
long long check(int mid){
cnt[0]=val[0]=0;
FOR(i,1,n){
cnt[i]=cnt[i-1];
val[i]=val[i-1];
if(A[i].weight>=mid) {
cnt[i]++;
val[i]+=A[i].value;
}
}
res=0;
FOR(i,1,m)res+=1LL*(cnt[B[i].R]-cnt[B[i].L-1])*(val[B[i].R]-val[B[i].L-1]);
return res;
}
int main() {
cin>>n>>m>>stan;
int R=0,L=2e9;
FOR(i,1,n)scanf("%d%d",&A[i].weight,&A[i].value),R=max(R,A[i].weight),L=min(L,A[i].weight);
FOR(i,1,m)scanf("%d%d",&B[i].L,&B[i].R);
int l=0,r=R;
long long ans=2e14;
while(l<=r) {
int mid=(l+r)>>1;
ll y=check(mid);
ans=min(ans,abs(y-stan));
if(y1;
else l=mid+1;
}
cout<return 0;
}
这道题是一道贪心题。它的基本决策如下:每一次我们肯定会选择车上人数最多的一条道路使用加速器。但是,如果有人迟到了,那么用了跟没用一样。因此我们就要在没有人会迟到,使用人数最多的那条道路上使用加速器。
因此我们的贪心决策就很明显了。接下来就是复杂度的问题。
我们可以看到如果我们对于每一个k都扫一遍进行决策的话,显然要用前缀和进行优化。而倒着使用前缀和显然会给我们的运算带来许多方便。对于这道题,由于我们进行的仅仅是判断与加减语句,复杂度常数非常小。因此O(n*k)的复杂度显然是能过的(虽然有一亿……)。
附上AC代码:
#include
#include
#include
#include
#define ShimaKZ 404 Not Found
#define M 100086
#define max sdjkl
#define min sdjll
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int n,m,k;
int dis[M];
int leave[M];
int arrive[M];
int sum[M];
int down[M];
int ans=0;
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return ainline void checkmax(int &a,int b){if(b>a)a=b;}
inline void checkmin(int &a,int b){if(bint main(){
cin>>n>>m>>k;
FOR(i,1,n-1)scanf("%d",&dis[i]);
FOR(i,1,m){
int t,a,b;
scanf("%d%d%d",&t,&a,&b);
checkmax(leave[a],t);
ans-=t;
down[b]++;
}
while(k--){
FOR(i,1,n)arrive[i]=max(arrive[i-1],leave[i-1])+dis[i-1];
int now=0;
DOR(i,n,2){
if(!dis[i-1])sum[i-1]=0;
else{
sum[i-1]=down[i];
if(arrive[i]>leave[i])sum[i-1]+=sum[i];
}
}
int id=0,max=0;
FOR(i,1,n-1){
if(sum[i]>max){
max=sum[i];
id=i;
}
}
dis[id]--;
}
FOR(i,1,n)arrive[i]=max(arrive[i-1],leave[i-1])+dis[i-1];
FOR(i,1,n)ans+=arrive[i]*down[i];
cout<return 0;
}
这次考试考得很迷……有很多不该错的地方犯了一些错误。其实第二题我完全没有必要进行对拍,你说前缀和优化和直接循环在结果上有什么区别呢?因此对于一些显然对的算法,就没有必要进行对拍了。对拍反而是徒劳。因此在这种情况下,不如多去考虑一下二分的边界以及自己的代码有没有什么漏洞,能不能用一些数据来进行hack。没有考出应有的水平,这是比不会写要伤很多的情况。下次要避免啊。