Codeforces #312 div2 only

2015-07-15 17:28:04

传送门

总结:... 打星的场 赛中 PP 了ABCE,后来C被FST(TLE)了。

  A题... 开场不是很认真... 23min+2WA - -。然后开了C题,用map搞过了,但复杂度n×(logn)^3,最后导致TLE,不能用map。

  随后过了B题。 由于D题比较长... 直接看了E,相了一会发现是线段树暴力,敲了30+min竟然过了编译就AC了,有点意外- -。

  D题没敲完。。赛后用了两种方法补了一下,细节很多,考虑需要很周全,是个好题!

 

A题:排序

#include <cstdio>
#include <ctime>
#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;
pii g[110];
vector<pii > g1,g2;

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        scanf("%d%d",&g[i].first,&g[i].second);
    }
    sort(g + 1,g + n + 1);
    int cnt1 = 0,cnt2 = 0;
    for(int i = 1; i <= n; ++i){
        if(g[i].first > 0){
            cnt2++;
            g2.push_back(g[i]);
        }
    }
    for(int i = n; i >= 1; --i){
        if(g[i].first < 0){
            cnt1++;
            g1.push_back(g[i]);
        }
    }
    int cnt = min(cnt1,cnt2);
    int sum = 0;
    for(int i = 0; i < cnt; ++i){
        sum += g1[i].second;
        sum += g2[i].second;
    }
    int maxx = 0;
    if(cnt1 != cnt2){
        if(cnt1 > cnt2) sum += g1[cnt].second;
        else sum += g2[cnt].second;
    }
    printf("%d\n",sum);
    return 0;
}
View Code

 

 

B题:小技巧

 

#include <cstdio>
#include <ctime>
#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;
const int MAXN = 100010;

int n;
int st[1000010],ed[1000010],cnt[1000010];

int main(){
    int a;
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        scanf("%d",&a);
        if(st[a] == 0) st[a] = i;
        ed[a] = i;
        cnt[a]++;
    }
    int tmax = 0;
    for(int i = 1; i <= 1000000; ++i) tmax = max(tmax,cnt[i]);
    int ans = INF,ansl,ansr;
    for(int i = 1; i <= 1000000; ++i) if(cnt[i] == tmax){
        if(ans > ed[i] - st[i] + 1){
            ans = ed[i] - st[i] + 1;
            ansl = st[i];
            ansr = ed[i];
        }
    }
    printf("%d %d\n",ansl,ansr);
    return 0;
}
View Code

 

 

C题:暴力+统计

  题意:有n(n<=10^5)个数 A[1] ~ A[n],每次操作可以把一个数乘上2或者除以2(向下取整),问最少多少步可以使得每个数都相等。

  思路:对于每个数,先看其所有能到达的数,以及到达这些数的最小步数(累加 vis[] 数组),方法是将这个数不断地除以2,根据到达的点来更新数组。

如果发现得到了奇数,那么我们可以开一个暂时变量tmp=奇数 - 1(这个值可以通过奇数/2*2得到),然后tmp不断乘以2,来表示能达到的数,然后更新数组。

(vis[] 数组是用来记录1~10^5每个数被 n 个数到达需要的总步数; can[] 数组是用来记录1~10^5每个数能被 n 个数中的多少个数达到)。最后我们发现漏掉了

一开始的值 A[i] 乘以2能到达的值,所以再处理一下。

  最后扫描 can[i] 数组,如果值为 n (即所有 n 个数都能到达这个数),那么用对应的 vis[i] 来更新 ans,取最小值。

 

#include <cstdio>
#include <ctime>
#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;
const int MAXN = 100010;

int n;
int A[MAXN];
int can[MAXN];
int vis[MAXN];

int main(){
    scanf("%d",&n);
    for(int o = 1; o <= n; ++o){
        scanf("%d",&A[o]);
        int tA = A[o] * 2;
        int cnt1 = 1;
        while(tA < MAXN){
            can[tA]++;
            vis[tA] += cnt1++;
            tA <<= 1;
        }
        tA = A[o];
        cnt1 = 0;
        while(tA){ //deal with the current A
            if((tA & 1) && tA > 1){ //odd number
                int cnt2 = 1; //inorder to get 2 as cnt2's initial value
                for(int i = tA / 2 * 2; i < MAXN; i *= 2){
                    cnt2++;
                    can[i]++;
                    vis[i] += cnt1 + cnt2;
                }
            }
            can[tA]++;
            vis[tA] += cnt1++;
            tA >>= 1;
        }
    }
    //for(int i = 1; i <= 20; ++i) if(can[i] == n) printf("vis[%d] : %d\n",i,vis[i]);
    int ans = INF;
    for(int i = 1; i < MAXN; ++i) if(can[i] == n){
        ans = min(ans,vis[i]);
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

 

D题:离散化 + 统计

  题意:给出一棵高度为 h (h <= 50) 的满二叉树(根的高度为1),节点编号从上到下,从左到右。现在假设某一个叶子节点(最底层节点)为这棵树的出口 exit,然后给出 q 个询问,格式是(i,l,r,ans),意思就是 exit 在第 i 层的祖先的编号是否在 [l,r] 之内,ans=1就表示“是”,ans=0就表示“否”。判断这 q 个问询是否会产生矛盾,如果不产生矛盾,那么 exit 是否唯一。

  思路:先把每个询问的区间的左右端点 “推” 到最底层,对于 ans=1 的区间,我们将他们全部 “交" 起来,如果在交的过程中发现两个区间相离,那么肯定是有矛盾的。若无矛盾,最后我们得到单个 ans=1 的区间与若干个 ans=0 的区间,开一个 map,对于 ans=1 的区间我们将左端点的map值+1,(右端点+1)的map值-1;对于 ans=0 的区间将左端点的值-1,将(右端点+1)的map值+1 。然后就是一种经典的线性扫描做法,从左到右扫描每个map点,累加 map 值,如果发现当前的累加值为1,那么判断这个点到下一个map点的距离,然后用Cnt累加距离。最后,Cnt 就代表了 exit 的个数,如果 Cnt == 0 那么也是矛盾的,如果 Cnt == 1 那么输出唯一解的位置(这个可以在过程中记录),如果 Cnt > 1 那么没有唯一解。

 

#include <cstdio>
#include <ctime>
#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;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 100010;

int h,q;
map<ll,int> mp;
map<ll,int>::iterator it,nxt_it;

int main(){
    ll ti,tl,tr,tans;
    scanf("%d%d",&h,&q);
    ll can_st = 1LL << (h - 1),can_ed = (1LL << h) - 1;
    ll tmin = 1LL << 60,tmax = 0;
    bool Cheat = false;
    for(int i = 1; i <= q; ++i){
        scanf("%I64d%I64d%I64d%I64d",&ti,&tl,&tr,&tans);
        tl = tl << (h - ti);
        tr = ((tr + 1) << (h - ti)) - 1;
        if(tans){ //can interval
            if(tr < can_st || can_ed < tl) Cheat = true;
            else{
                can_st = max(can_st,tl);
                can_ed = min(can_ed,tr);
            }
        }
        else{ //cannot interval
            mp[tl] -= 1;
            mp[tr + 1] += 1;
            tmin = min(tmin,tl);
            tmax = max(tmax,tr);
        }
    }
    if(Cheat){
        printf("Game cheated!\n");
        return 0;
    }
    ll sta = 0,Cnt = 0,ans_pos;
    mp[can_st] += 1;
    mp[can_ed + 1] -= 1;
    for(it = mp.begin(); it != mp.end(); it++){
        sta += (*it).second;
        if(sta == 1){
            nxt_it = it; nxt_it++;
            if(nxt_it == mp.end()) continue;
            ll cur = (*nxt_it).first - (*it).first;
            Cnt += cur;
            if(cur == 1) ans_pos = (*it).first;
        }
    }
    if(Cnt == 0) printf("Game cheated!\n");
    else if(Cnt == 1) printf("%I64d\n",ans_pos);
    else printf("Data not sufficient!\n");
    return 0;
}
View Code

 

 

 

E题:线段树(多棵)

  题意:给出一个长度为 n 的字符串(n <= 10^5),q(q <= 50000)个操作,每个操作的形式:(i,j,k),如果 k=0 那么将区间 [i,j] 内的字母降序排序,如果 k=1 那么将区间 [i,j] 内的字母升序排序。输出进行完所有操作的字符串。限时:5秒。

  思路:既然限时这么宽松... 不妨直接上数据结构。看到有用 set、map 的。

  我的思路是开26棵线段树,来记录每个字母在字符串中的位置。因为排序后相同的字母是排列在一起的,很有规律,可以最多进行26次区间修改来实现操作。其他没什么技巧的,纯代码题。

 

#include <cstdio>
#include <ctime>
#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;
const int MAXN = 100010;

int n,q;
char s[MAXN];
int tsum[27][MAXN << 2],flag[27][MAXN << 2];
int Len[MAXN << 2];
char ans[MAXN];

void Push_down(int id,int p){
    if(flag[id][p] != -1){
        flag[id][p << 1] = flag[id][p];
        flag[id][p << 1|1] = flag[id][p];
        if(flag[id][p] == 1){
            tsum[id][p << 1] = Len[p << 1];
            tsum[id][p << 1|1] = Len[p << 1|1];
        }
        else if(flag[id][p] == 0){
            tsum[id][p << 1] = tsum[id][p << 1|1] = 0;
        }
        flag[id][p] = -1;
    }
}

void Update(int id,int a,int b,int c,int p,int l,int r){
    if(a <= l && r <= b){
        flag[id][p] = c;
        if(flag[id][p] == 1) tsum[id][p] = Len[p];
        else if(flag[id][p] == 0) tsum[id][p] = 0;
        return;
    }
    Push_down(id,p);
    int mid = getmid(l,r);
    if(a <= mid) Update(id,a,b,c,p << 1,l,mid);
    if(b > mid) Update(id,a,b,c,p << 1|1,mid + 1,r);
    tsum[id][p] = tsum[id][p << 1] + tsum[id][p << 1|1];
}

int Query(int id,int a,int b,int p,int l,int r){
    if(a <= l && r <= b){
        return tsum[id][p];
    }
    Push_down(id,p);
    int mid = getmid(l,r);
    int res = 0;
    if(a <= mid) res += Query(id,a,b,p << 1,l,mid);
    if(b > mid) res += Query(id,a,b,p << 1|1,mid + 1,r);
    return res;
}

void Get(int p,int l,int r){
    Len[p] = r - l + 1;
    if(l == r) return;
    int mid = getmid(l,r);
    Get(p << 1,l,mid);
    Get(p << 1|1,mid + 1,r);
}

void Print(int id,int p,int l,int r){
    if(tsum[id][p] == 0) return;
    if(l == r){
        ans[l] = 'a' + id;
        return;
    }
    Push_down(id,p);
    int mid = getmid(l,r);
    Print(id,p << 1,l,mid);
    Print(id,p << 1|1,mid + 1,r);
}

int main(){
    memset(flag,-1,sizeof(flag));
    int a,b,k;
    scanf("%d%d",&n,&q);
    Get(1,1,n);
    scanf("%s",s + 1);
    for(int i = 1; i <= n; ++i){
        int id = s[i] - 'a';
        Update(id,i,i,1,1,1,n);
    }
    int cnt[30];
    for(int o = 1; o <= q; ++o){
        scanf("%d%d%d",&a,&b,&k);
        for(int i = 0; i < 26; ++i){
            cnt[i] = Query(i,a,b,1,1,n);
            if(cnt[i]){
                Update(i,a,b,0,1,1,n);
            }
        }
        int st = a;
        if(k == 1){
            for(int i = 0; i < 26; ++i) if(cnt[i]){
                Update(i,st,st + cnt[i] - 1,1,1,1,n);
                st += cnt[i];
            }
        }
        else{
            for(int i = 25; i >= 0; --i) if(cnt[i]){
                Update(i,st,st + cnt[i] - 1,1,1,1,n);
                st += cnt[i];
            }
        }
    }
    for(int i = 0; i < 26; ++i){
        Print(i,1,1,n);
    }
    ans[n + 1] = '\0';
    printf("%s\n",ans + 1);
    return 0;
}
View Code

 

posted @ 2015-07-15 20:19  Naturain  阅读(228)  评论(0编辑  收藏  举报