2014多校联合训练第一场(组队训练)

  这是我们第二次组队训练,毕竟经验不足,加上水平不够,导致我们各种被碾压。

  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;
}

  

原文地址:https://www.cnblogs.com/monmonde/p/3912693.html