Codeforces #307 div2

2015-06-14 17:13:05

传送门

总结:比赛的时候有点逗比... C wa了一发后弃疗了,过掉了D,最后D因为一个巨坑的数据FST掉了... QAQ

 

A题:简单排序

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;

int n;

struct node{
    int a,id,pos;
}p[2010];

bool cmp(node a,node b){
    return a.a > b.a;
}

bool cmp_id(node a,node b){
    return a.id < b.id;
}

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        scanf("%d",&p[i].a);
        p[i].id = i;
    }
    sort(p + 1,p + n + 1,cmp);
    int P = 0;
    p[1].pos = 1;
    for(int i = 2; i <= n; ++i){
        if(p[i].a == p[i - 1].a){
            p[i].pos = p[i - 1].pos;
        }
        else p[i].pos = i;
    }
    sort(p + 1,p + n + 1,cmp_id);
    for(int i = 1; i <= n;  ++i){
        printf("%d ",p[i].pos);
    }
    puts("");
    return 0;
}
View Code

 

B题:枚举

思路:因为字母可以随便交换,没有次数限制,所以先统计出A串中各个字母的出现次数,然后枚举交换位置后的A串包含几个B串,然后在每次枚举时算出此时最多能包含多少个C串,计算出最大值即可。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;

char a[100010],b[100010],c[100010];
int cnt[300],cntx[300],cntb[300],cntc[300];
int len1,len2,len3;

bool Check(int m){
    for(int i = 0; i < 300; ++i) if(cntb[i]){
        if(cnt[i] < cntb[i] * m) return false;
    }
    return true;
}

int Cal(int m){
    int tmin = INF;
    memcpy(cntx,cnt,sizeof(cnt));
    for(int i = 0; i < 300; ++i) if(cntb[i]){
        cntx[i] -= cntb[i] * m;
    }
    for(int i = 0; i < 300; ++i) if(cntc[i]){
        tmin = min(tmin,cntx[i] / cntc[i]);
    }
    return tmin;
}

int main(){
    scanf("%s%s%s",a,b,c);
    len1 = strlen(a);
    len2 = strlen(b);
    len3 = strlen(c);
    for(int i = 0; i < len1; ++i){
        cnt[a[i]]++;
    }    
    for(int i = 0; i < len2; ++i){
        cntb[b[i]]++;
    }
    for(int i = 0; i < len3; ++i){
        cntc[c[i]]++;
    }
    int ans = 0,ansx = -1,ansy = -1;
    int top = len1 / len2;
    for(int i = 0; i <= top; ++i){
        if(Check(i)){
            int v = Cal(i);
            if(i + v > ans){
                ans = i + v;
                ansx = i;
                ansy = v;
            }
        }
    }
    if(ans == 0){
        printf("%s\n",a);
        return 0;
    }
    else{
        for(int i = 0; i < ansx; ++i) printf("%s",b);
        for(int i = 0; i < ansy; ++i) printf("%s",c);
        memcpy(cntx,cnt,sizeof(cnt));
        for(int i = 0; i < len2; ++i) cntx[b[i]] -= ansx;
        for(int i = 0; i < len3; ++i) cntx[c[i]] -= ansy;
        for(int i = 0; i < 300; ++i) if(cntx[i]){
            for(int j = 0; j < cntx[i]; ++j) printf("%c",i);
        }
        puts("");
    }
    return 0;
}
View Code

 

C题:二分

题意:有n堆盒子,第i堆有ai个盒子,现在有m个人在第1堆的左边,每一秒每个人可以走向右边的堆或者搬掉当前堆的一个盒子,问最少时间搬完。

思路:二分答案,然后倒着考虑,首先考虑最后一堆,在当前枚举的时间下,计算最少要派多少个人到最后一堆才能搬完,式子表达为

num = ceil(a[n] / (cur_time - n)) ceil表示向上取整,,我们发现如果式子不是整除的话,有些人会有空闲的情况,那么我们可以让这些人在来到第 n堆之前拿前面一些堆的盒子,这个值为:remain = num * (cur_time - n) - a[n]。这样我们就获得了一种贪心算法,从第n堆开始考虑到第1堆,不断累加 remain 值,如当前堆比 remain 值大,那么减掉再算出需要的人数,然后更新 remain 值。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;

int n,m;
ll A[100010];

bool Check(ll v){
    ll tm = m,rem = 0;
    for(int i = n; i >= 1; --i) if(A[i]){
        if(A[i] <= rem){
            rem -= A[i];
            continue;
        }
        ll tA = A[i] - rem;
        if(v - i <= 0) return false;
        ll ned = (ll)ceil(1.0 * tA / (v - i));
        rem = ned * (v - i) - tA;
        tm -= ned;
        if(tm < 0) return false;
    }
    return true;
}

int main(){
    scanf("%d%d",&n,&m);
    ll sum = 0;
    for(int i = 1; i <= n; ++i){
        scanf("%I64d",A + i);
    }
    ll l = 0,r = 1LL << 60;
    while(l < r){
        ll mid = getmid(l,r);
        if(Check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%I64d\n",l);
    return 0;
}
View Code

 

D题:数学统计,快速计算Fib / 矩阵快速幂

题意:有n个数,a1、a2~an,每个数的范围为[0~2^l),问有多少种n个数的数列满足:  这个条件,答案对 m 取模。

思路:独立地考虑 k 的每一个二进制位,如果为1,那么必定存在某两个相邻的数在这一位均为1;如果为0,则反之。

【用dp可以计算出对于某个二进制位,不存在任意两个相邻的数在该位均为1的情况数。dp[i][j] 表示第i个数在该位为j,那么就有转移公式:

,答案为 ,我们发现这个答案就是第 n+2 个斐波那契数!

由于 n 很大,所以可以用矩阵快速幂解决,就不赘述了。当然还可以用斐波那契另外一个公式:

所以:,这样就可以用记忆化搜索了!】

最后,只要逐位考虑 k 的每个二进制位,如果是1,答案乘上 2^n - F(n+2);如果是0,答案乘上F(n+2) 。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;

ll n,k,l,m,sum;
map<ll,ll> fib;

ll Fib(ll v){
    if(fib.count(v)) return fib[v];
    ll k = v / 2;
    if(v & 1) fib[v] = (Fib(k+1)*Fib(k+1)%m + Fib(k)*Fib(k)%m) % m;
    else fib[v] = (Fib(k+1)*Fib(k)%m + Fib(k)*Fib(k-1)%m) % m;
}

ll Qpow(ll x,ll y){
    ll res = 1;
    while(y){
        if(y & 1) res = res * x % m;
        x = x * x % m;
        y >>= 1;
    }
    return res;
}

int main(){
    scanf("%I64d%I64d%I64d%I64d",&n,&k,&l,&m);
    fib[0] = fib[1] = 1;
    sum = Fib(n + 2);
    ll ans = 1;
    for(int i = l; i <= 63; ++i){
        if(k & (1LL << i)){
            ans = 0;
            break;
        }
    }
    if(ans == 0){
        printf("0\n");
        return 0;
    }
    ll top = Qpow(2,n);
    for(int i = 0; i < l; ++i){
        ll v = 1LL << i;
        if(k & v){
            ans *= (top - sum + m);
            ans %= m;
        }
        else ans = (ans * sum) % m;
    }
    printf("%I64d\n",ans % m);
    return 0;
}
View Code

 

E题:分块,排序

题意:给出n个数的数列(n <= 5*10^5),有q个查询(q <= 5*10^4),有两种查询,第一种是给一个区间加上一个x,第二种查询给出 y ,输出最大 j - i,

其中 i 和 j 满足 ai = aj = y 。

思路:将 n 个数分成若干块,每块 sqrt(n)个数,然后快内排序。开一个数组 plus[] 用来保存每个块整块的增加值,表示块内所有元素都要加上这个值。

对于第一种查询,考虑区间涉及的所有块,如果这块完全包含于区间,则给该块的 plus 值加上x;否则暴力更改块内被包含在区间内的数,然后重新排序

(这个过程往往被称为块内重建)。

对于第二种查询,逐个考虑每一块,用二分查找找出下标最大的 y 值和下标最小的 y 值,这就要求将每个数保存成 pair 类型,first 为数值,second 为下标。

下标最大的 y 值可以用 upper_bound()位置减1 处理,下标最小的 y 值可以用 lower_bound 处理。(具体看程序)就这样维护出 y 的最小下标和 y 的最大标答案就是最大下标 - 最小下标。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB push_back

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<long long,int> pli;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 500010;
const int MAX_SQR = 1000;

int n,q,block,bid[MAXN];
vector<pli > A[MAX_SQR];
ll val[MAXN],PL[MAX_SQR];
int L[MAX_SQR],R[MAX_SQR];

inline bool cmp(pli a,pli b){
    if(a.first == b.first) return a.second < b.second;
    return a.first < b.first;
}

inline void Rebuild(int id){
    A[id].clear();
    for(int i = L[id]; i <= R[id]; ++i) A[id].PB(MP(val[i],i));
    sort(A[id].begin(),A[id].end(),cmp);
}

int main(){
    int a,b,c,d;
    scanf("%d%d",&n,&q);
    block = (int)sqrt(1.0 * n); //每块长度
    int bcnt = n / block + (n % block ? 1 : 0);
    for(int i = 1; i <= n; ++i){
        scanf("%I64d",&val[i]);
        bid[i] = (i - 1) / block + 1;
        A[bid[i]].PB(MP(val[i],i));
    }
    for(int i = 1; i <= bcnt; ++i){ //计算边界
        L[i] = (i - 1) * block + 1;
        R[i] = i * block;
        sort(A[i].begin(),A[i].end());
    }
    for(int i = 1; i <= q; ++i){
        scanf("%d",&a);
        if(a == 1){
            scanf("%d%d%d",&b,&c,&d);
            int sid = (b - 1) / block + 1,eid = (c - 1) / block + 1; //首末块的编号
            if(sid == eid){
                for(int j = b; j <= c; ++j) val[j] += d;
                Rebuild(sid);
            }
            else{
                if(b > L[sid]){
                    for(int j = b; j <= R[sid]; ++j) val[j] += d;
                    Rebuild(sid++);
                }
                if(c < R[eid]){
                    for(int j = L[eid]; j <= c; ++j) val[j] += d;
                    Rebuild(eid--);
                }
                for(int j = sid; j <= eid; ++j) PL[j] += d;
            }
        }
        else{
            scanf("%d",&b);
            int pmax = -1,pmin = INF;
            for(int j = 1; j <= bcnt; ++j){
                ll cur = b - PL[j];
                int num = lower_bound(A[j].begin(),A[j].end(),MP(cur,0)) - A[j].begin();
                int pos = A[j][num].second;
                if(A[j][num].first == cur && num < A[j].size()){
                    pmin = min(pmin,pos);
                    num = upper_bound(A[j].begin(),A[j].end(),MP(cur,INF)) - A[j].begin() - 1;
                    pos = A[j][num].second;
                    //printf("%d : pos2 %d\n",j,pos);
                    pmax = max(pmax,pos);
                }
            }
            if(pmax == -1) printf("-1\n");
            else printf("%d\n",pmax - pmin);
        }
    }
    return 0;
}
View Code

 

posted @ 2015-06-14 18:25  Naturain  阅读(148)  评论(0编辑  收藏  举报