这是我们第二次组队训练,毕竟经验不足,加上水平不够,导致我们各种被碾压。
A - Couple doubi:
这道题是道比较水的数论。但我们都没想出来要怎么做。后来是potaty提议打个表看看,然后lmz打出表后发现了规律。我还没细看,待研究后再补全。
D - Task:
这道题一看就知道是个贪心(现在只要是有deadline的题我都觉得是贪心了)。虽然想出来了,但还是不会严格证明为什么只要取满足task的且y最小(y相等时x最小)的machine就行了。
我的做法是把所有machine放进第machine_i_y个桶中,这个桶用set来实现。然后按照x的大小顺序从大到小(x相等时y从大到小)依次取task,每次找到满足最合适的machine。最合适的machine条件上面已经给出(貌似先看x最小,再看y最小也行,不过我的for每个machine再for每个y,如果改成for每个x,时间过不去)。但为什么task是从大到小来取呢?
题目中钱的公式是500*xi+2*yi,而yi<=100,xi>1。可以发现,只要xi>xj,即xi>=xj+1,那么无论怎么取yi和yj,500*xi+2*yi始终是大于500*xj+2*yj的。所以这样取task能保证钱是最多的。鉴于每个machine只能取一个task,如果不取x(或y)偏大的task,而去取x(或y)较小的task,这明显是一种对money的浪费,而且也没有完成更多的task,所以不会有上述做法的反例存在。综上,上述做法是对的。
第一次写博客,若有不明白的可以指出,我会尽力解答的。
#include <iostream> #include <cstdio> #include <cstdlib> #include <queue> #include <algorithm> #include <set> #include <utility> #define FOR(i,l,r) for(int i=(l);i<=(r);i++) #define FORD(i,r,l) for(int i=(r);i>=(l);i--) #define rep(i,n) for(int i=0;i<(n);i++) using namespace std; typedef long long LL; pair<int,int> mac[100001],task[100001]; int n,m; int main(){ while (~scanf("%d%d",&n,&m)) { FOR (i,1,n) scanf("%d%d",&mac[i].second,&mac[i].first); sort(mac+1,mac+1+n); FOR (i,1,n) swap(mac[i].second,mac[i].first); FOR (i,1,m) scanf("%d%d",&task[i].first,&task[i].second); sort(task+1,task+1+m); multiset<int> q[101]; multiset<int>::iterator it; FOR (i,1,n) q[mac[i].second].insert(mac[i].first); LL ans=0; int ansgs=0; int j; FORD (i,m,1) { for (j=task[i].second; j<=100; j++) { if (!q[j].empty()) if ((it=q[j].lower_bound(task[i].first)) != q[j].end()) break; } if (j==101) continue; q[j].erase(it); ansgs++; ans+=500*task[i].first+2*task[i].second; } /*bool vis[100001]; FORD (i,m,1) { FOR (j,1,n) if (mac[j].first>=task[i].first&&mac[j].second>=task[i].second&&!vis[j]) { vis[j]=1; ansgs++; ans+=500*task[i].first+2*task[i].second; break; } }*/ cout<<ansgs<<" "<<ans<<endl; } return 0; }
F - Shooting:
这道题苦思良久没想出来,在看了题解之后一下就明白了。说实话,非常佩服出题人,能想出这样的题来。
题目大意:
有n个target,每次站在x处射击,最多可以射击k个target。K是由公式K = ( a * Pre + b ) % c算出的,Pre是上一次shooting的得分。具体可参考下图:
target被射中不会消失。每次的得分是所有被射中的target的距离之和。如果上次的得分大于P,那么这次的得分翻番。求每次的得分。
解题思路:把距离Di(1<=i<=N)离散化,然后将target按距离从小到大加入函数式线段树中(用左区间点+1,右区间点-1的方式)。然后每次二分一下求出第k个target的位置,二分是利用线段树来算出x处有多少个线段(target)。找到位置之后就很好办了。read得分和read线段的个数的操作是一样的。
还有种做法是按照X坐标离散化。两种方法本质是差不多的,所以就不赘写了。
时间复杂度为O(N*logN+2*N*logX+M*(logN*logX+logX)). 感觉3s的时间会不够的样子...
#include <cstdio> #include <iostream> #include <algorithm> #include <cstdlib> #include <cstring> #define debug(x) cout<<#x<<" = "<<x<<endl #define FOR(i,l,r) for(int i=(l);i<=(r);i++) #define rep(i,n) for(int i=0;i<(n);i++) #define foreach(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++) #define LC(t) ((t)?(t->o[0]):0) #define RC(t) ((t)?(t->o[1]):0) using namespace std; typedef long long LL; const int maxn=100001; int X; struct btree{ btree *o[2]; int cnt; LL d; }tbt[maxn*50]; int tpo; btree *newnode() { btree *p=&tbt[tpo++]; p->o[0]=p->o[1]=0; p->cnt=p->d=0; return p; } btree *sr(btree *t,int l,int r,int x,int v,int d) { btree *p=newnode(); if (t) *p=*t; p->cnt+=v; p->d+=d; if (l==r) return p; int mid=l+r>>1; if (x<=mid) p->o[0]=sr(p->o[0],l,mid,x,v,d); else p->o[1]=sr(p->o[1],mid+1,r,x,v,d); return p; } int red1(btree *t,int l,int r,int ll,int rr) { if (!t) return 0; if (ll<=l&&r<=rr) return t->cnt; int mid=l+r>>1,ret=0; if (ll<=mid) ret+=red1(t->o[0],l,mid,ll,rr); if (rr>mid) ret+=red1(t->o[1],mid+1,r,ll,rr); return ret; } LL red2(btree *t,int l,int r,int ll,int rr) { if (!t) return 0; if (ll<=l&&r<=rr) return t->d; int mid=l+r>>1; LL ret=0; if (ll<=mid) ret+=red2(t->o[0],l,mid,ll,rr); if (rr>mid) ret+=red2(t->o[1],mid+1,r,ll,rr); return ret; } struct target{ int l,r,d; bool operator< (const target &a) const { return d<a.d; } }tar[maxn]; int n,m,P; btree *bt[maxn]; void init() { tpo=0; X++; } int ef(int x,int k) { int l=1,r=n,mid; while (l<=r) { mid=l+r>>1; if (red1(bt[mid],1,X,1,x)<=k) l=mid+1; else r=mid-1; } return r; } int main(){ for (int x,a,b,c,z;scanf("%d%d%d%d",&n,&m,&X,&P)!=EOF;) { init(); FOR (i,1,n) scanf("%d%d%d",&tar[i].l,&tar[i].r,&tar[i].d); sort(tar+1,tar+1+n); FOR (i,1,n) bt[i]=sr(bt[i-1],1,X,tar[i].l,1,tar[i].d), bt[i]=sr(bt[i],1,X,tar[i].r+1,-1,-tar[i].d); LL pre=1,ans,k; for (int i=1;i<=m;i++,pre=ans) { scanf("%d%d%d%d",&x,&a,&b,&c); k=((LL)a*pre+(LL)b)%c; z=ef(x,k); ans=red2(bt[z],1,X,1,x); if (pre>P) ans*=2; printf("%I64d ",ans); } } return 0; }