1. 程式人生 > >2012 ACM/ICPC 長春賽區網路賽

2012 ACM/ICPC 長春賽區網路賽

參考地址:http://www.haogongju.net/art/1637871

題意:有兩種操作,一種是更新區間a~b中 a <= i <= b and (i - a) % k == 0 的點加上c , 一種是詢問Aa 的value

思路:明顯的線段樹 , 但是依然跪倒啊、、、cnt[i][k] = c 代表當前區間i 每隔k個字元累加c, 然後就是更新,如果存在要更新的區間那麼直接更新就行,否則左孩子依舊更新,右孩子找到第一個屬於a~b區間並且滿足(i - a)%k==0 的點,繼續更新,詢問的時候要把 所有cnt[i][k] = c的影響算進去

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lson l , m , rt<<1
#define rson m + 1 , r , rt<<1|1
const int maxn = 50005;
typedef long long ll;
ll cnt[50005*3][11];   //cnt[i][k]=c當前區間i每隔k個字元累加c
ll a[maxn];
void build(int l , int r , int rt) {
    int i;
    for(i = 1 ; i <= 10 ; i ++)
    cnt[rt][i] = 0;
    if(l == r) return;
    int m;
    m = (l + r) >> 1;
    build(lson);
    build(rson);    
}
void update(int l , int r , int rt , int L , int R , int k , ll c) {
    //printf("%d %d %d , %d %d %d %d\n",l,r,rt,L,R,k,c);
    //system("pause");
    if(L > R) return;
    if(l == L && r == R) {
        //printf("~~!");
        cnt[rt][k] += c;
        return;    
    }
    int m;
    m = (l + r) >> 1;
    if(m >= R) {
        update(lson , L , R , k , c);    
    } else if(m < L) {
        update(rson , L , R , k , c);    
    } else {
        update(lson , L , m , k , c);
        int st = (k - (m + 1 - L)%k)%k; 
        //printf("st = %d\n",st);
        update(rson , m + 1 + st , R , k , c);   
    }
}
ll query(int l , int r , int rt , int pos) {
    //printf("%d %d %d\n",l,r,rt);
    ll ans = 0;
    for(int i = 1 ; i <= 10 ; i ++) {
        if((pos - l)%i == 0) 
        ans += cnt[rt][i];    
    }
    if(l == r) return ans;
    int m = (l + r) >> 1; 
    if(pos <= m) return query(lson , pos) + ans;
    else return query(rson , pos) + ans;     
}
int main() {
    int  i , l , r , k , n , m , pos , op;
    ll c;
    while(~scanf("%d",&n)) {
        for(i = 1 ; i <= n ; i ++)
        scanf("%lld",&a[i]);
        scanf("%d",&m);
        build(1 , n , 1);
        while(m --) {
            scanf("%d",&op);
            if(op == 2) {
                scanf("%d",&pos);
                printf("%lld\n",a[pos]+query(1 , n , 1 , pos));    
            } else {
                scanf("%d %d %d %lld",&l,&r,&k,&c);
                update(1 , n , 1 , l , r , k , c);    
            }
        }
    }    
}


題意:看到題目不要以為是博弈、、、 呵呵 , 是貪心、、、就是Alice有一些矩形,已知他們的長寬,Bob同樣也有,求Alice最大可能覆蓋Bob的矩形的個數(長寬都分別比塔大)

思路:具體參考 資訊平臺站的題解 http://acmicpc.info/  就是先把兩個人的矩形按照h 、 w 進行排序,然後根據Alice的h去找滿足能覆蓋Bob的h然後在這些Bob的矩形中,找出Bob矩形的w剛好小於等於Alice的w的矩形,這是比較理想的一次覆蓋,因為要留下較小的給其他矩形覆蓋留更多的選擇餘地。這裡用Muliset來搞。

#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
#define ft first
#define sd second
using namespace std;
const int maxn = 100010;
typedef pair<int , int> pii;
pii a[maxn] , b[maxn];
int main() {
    int T , n , i , j;
    scanf("%d",&T);
    while(T --) {
        scanf("%d",&n);
        for(i = 0 ; i < n ; i ++) scanf("%d%d",&a[i].ft,&a[i].sd);
        for(i = 0 ; i < n ; i ++) scanf("%d%d",&b[i].ft,&b[i].sd);
        sort(a , a + n);
        sort(b , b + n);
        multiset<int> mt;
        multiset<int>::iterator it;
        mt.clear();
        int ans = 0;
        for(i = 0 , j = 0; i < n ; i ++) {
            while(j < n && a[i].ft >= b[j].ft) {
                mt.insert(b[j].sd);
                j ++;
            }    
            if(mt.size()>=1) {
                it = mt.lower_bound(a[i].sd); //大於等於k的第一個元素的位置 
                if(it == mt.end()) it--;
                if(*it > a[i].sd && it != mt.begin()) it--;    
                if(*it <= a[i].sd) {
                    mt.erase(*it);
                    ans ++;   
                }
            }
        }
        printf("%d\n",ans); 
    }
}


還有一個版本 是把兩人的矩形放在一起h w id進行排序,其中排序的時候過載的小於號的方法注意學習,然後h肯定是升序的,然後Bob的w插入到multiset,接著如果遇到Alice的矩形,那麼h肯定能覆蓋multiset中的Bob的h(沒有插入h,因為h是升序排的),剩下的就是要根據此時Alice的w去找multiset中剛好比它小一點或者等於它的進行覆蓋。

#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct rec {
    int h , w , id;
    bool operator <(const rec &b) const {
        if(h != b.h) return h < b.h;
        if(w != b.w) return w < b.w;
        return id < b.id;        
    }    
};
multiset<int> mt;
multiset<int>::iterator it;
rec a[maxn<<1];
int main() {
    int T , n , i , id;       
    scanf("%d",&T);
    while(T --) {
        scanf("%d",&n);
        for(i = 0 ; i < (n<<1) ; i ++) {
            scanf("%d%d",&a[i].h,&a[i].w);
            if(i >= n) a[i].id = 0;
            else a[i].id = 1;    
        } 
        sort(a , a +(n<<1));
        mt.clear();
        int ans = 0;
        for(i = 0 ; i < (n<<1) ; i ++) {
            if(a[i].id == 0) mt.insert(a[i].w);
            else {
                if(!mt.empty()) {
                    if(*mt.begin() <= a[i].w) {
                        it = mt.upper_bound(a[i].w);
                        it--;
                        mt.erase(*it);    
                        ans ++;
                    }    
                }   
            }    
        }
        printf("%d\n",ans);   
    }
}


題意:題意有歧義,題解是狀壓、、不會、、貌似模擬可以搞、、、 就是每次消除棧頂元素和向下數6個長度的距離中和其相同的元素,如果最後能消除所有的棧中元素那麼輸出1,否則輸出0

思路:模擬即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
int main() {
    int N , i , j;
    int num;
    int S[1010];
    int vis[1010];
    while(~scanf("%d",&N)) {
        int top = 0;
        for(i = 1 ; i <= N ; i ++) {
            scanf("%d",&num);
            S[top++] = num; 
        }
        if(N%2==1) {
            printf("0\n");
            continue;    
        }
        bool flag;
        memset(vis , 0 , sizeof(vis));
        int cnt = 0;
        int ff = top - 1;
        while(cnt != N) {
            flag = false;
            for(i = top - 1 ; i >= 0 ; i --) {
                if(!vis[i]) {
                    ff = i;
                    for(j = 1 ; j <= 6 ; j ++) {
                        if(S[ff] == S[ff-j] && ff - j >=0) {
                            vis[ff] = 1;
                            vis[ff - j] = 1;
                            flag = true;
                            cnt += 2;
                            break;
                        }    
                    }
                    if(flag) {
                        break;    
                    }  else {
                        goto loop;    
                    }
                }    
            } 
        }
        loop:
        if(!flag) printf("0\n");
        else printf("1\n");
    }    
} 

題意:給出一些木棍的長度最多有15根,然後所有的木棒都要用上,求能組成三角形的種類

思路:參考http://www.cnblogs.com/yejinru/archive/2012/09/08/2676899.html 就是把15根木棍放到三條邊的位置上,最大複雜度是3^15,每次搜的時候把三條邊排序分別放在d[0] , d[1] , d[2]中,然後用pair 兩條小邊和map進行判重,這樣搜貌似正好是可以超時,根據對稱性直接把a[1]放在第一條邊的位置,那麼複雜度減小為3^14可以卡過去、、、

#include<iostream>
#include<cstdio>
#include<set>
#include<cstring>
using namespace std;
int a[20] , d[5] , ans , sum , n;
set<pair<int , int> > myset;
void dfs(int i , int suma , int sumb , int sumc) {
    if(i == n+1) {
        if(suma < sumb) {
            d[0] = suma;
            d[1] = sumb;    
        } else {
            d[0] = sumb;
            d[1] = suma;    
        }
        if(sumc < d[0]) d[0] = sumc;
        if(sumc > d[1]) d[1] = sumc;
        d[2] = sum - d[0] - d[1];
        swap(d[2] , d[1]);
        if(d[0] + d[1] <= d[2]) return;
        printf("%d %d %d\n",d[0],d[1],d[2]);
        if(myset.find(make_pair(d[0] , d[1]))!=myset.end()) return;
        ans++;
        myset.insert(make_pair(d[0] , d[1]));
        return;
    }    
    dfs(i + 1 , suma + a[i] , sumb , sumc);
    dfs(i + 1 , suma , sumb + a[i] , sumc);
    dfs(i + 1 , suma , sumb , sumc + a[i]);
}
int main() {
    int T , i;
    scanf("%d",&T);
    while(T --) {
        scanf("%d",&n);
        sum = 0;
        for(i = 1 ; i <= n ; i ++) {
            scanf("%d",&a[i]);
            sum += a[i];    
        }    
        myset.clear();
        ans = 0;
        dfs(2 , a[1] , 0 , 0);
        printf("%d\n",ans);
    }    
}