zoukankan      html  css  js  c++  java
  • 近期刷题记录表

    9月14日:  

    luogu P1627 [CQOI2009]中位数 

    题意:给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

    题解:根据中位数的性质,将大于b的数记为1,小于b的数记为-1,区间和为0的奇数序列即符合题意,再计数即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, b, a[N], lsum[N], rsum[N]; 
    int l[N], r[N], minn = N, maxx = 0;
    int pos;
    ll ans = 0;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        n = read(); b = read();
        rep(i, 1, n){ 
            a[i] = read();
            if(a[i] < b) a[i] = -1;
            if(a[i] == b) a[i] = 0, pos = i;
            if(a[i] > b) a[i] = 1;
        }
        rep(i, pos+1, n) rsum[i] = rsum[i-1] + a[i], minn = min(minn, rsum[i]+n);
        per(i, pos-1, 1) lsum[i] = lsum[i+1] + a[i], minn = min(minn, lsum[i]+n);
        l[n] = r[n] = 1;
        rep(i, pos+1, n) r[rsum[i]+n]++;
        per(i, pos-1, 1) l[lsum[i]+n]++;
        maxx = 2*n-minn;
        rep(i, minn, maxx){
            ans += (ll)l[i] * r[2*n-i];
        } 
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    luogu P3407 散步

    题意:数轴上有n个人,每秒钟在给定的方向(向东或向西)移动一个距离,当一个人与一个人相遇时两人不再移动,求t秒后,指定m个人所在的位置。

    题解:预处理出相遇点,然后二分答案与pos+t进行比较即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    const ll inf = 4557430888798830399;
    ll n, t, q;
    ll s[N], k = 0;
    struct people{ll pos, dic, id; } a[N];
    bool mycmp(people a, people b) {return a.pos < b.pos; }
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        n = read(); t = read(); q = read();
        rep(i, 1, n) a[i].pos = read(), a[i].dic = read(), a[i].id = i;
        sort(a+1, a+n+1, mycmp);
        rep(i, 2, n){
            if(a[i].dic == 2 && a[i-1].dic == 1) s[++k] = (a[i].pos+a[i-1].pos)>>1;
        }  
        s[0] = -inf, s[k+1] = inf;
        rep(i, 1, q){
            ll x = read();
            ll posx = a[a[x].id].pos, flag = a[a[x].id].dic;
            ll l = 1, r = k, mid;
            while(l < r){
                mid = (l+r)>>1;
                if(flag == 1){
                    if(s[mid] < posx) l = mid+1;
                    if(s[mid] > posx) r = mid;
                }
                if(flag == 2){
                    if(mid == l) mid++;
                    if(s[mid] < posx) l = mid;
                    if(s[mid] > posx) r = mid-1;
                }
            }
            mid = l;
            if(s[mid] < posx && flag == 2){
                if(s[mid+1] < posx && s[mid+1] != inf) mid++;
            }
            if(s[mid] > posx && flag == 1){
                if(s[mid-1] > posx && s[mid-1] != inf) mid--; 
            }
            if(flag == 1){
                if(s[mid] > posx+t && s[mid] > posx) printf("%lld
    ", posx+t);
                else if(s[mid] < posx) printf("%lld
    ", posx+t);
                else if(s[mid] < posx+t && s[mid] > posx) printf("%lld
    ", s[mid]);
                else printf("%lld
    ", s[mid]);
            }
            if(flag == 2){
                if(s[mid] < posx-t && s[mid] < posx) printf("%lld
    ", posx-t);
                else if(s[mid] > posx-t && s[mid] < posx) printf("%lld
    ", s[mid]);
                else if(s[mid] > posx) printf("%lld
    ", posx-t); 
                else printf("%lld
    ", s[mid]);
            }
        }
        return 0;
    }
    View Code

     9月15日:

    HLOJ 糖果传递

    题意:有n个小朋友坐成一圈,每人有ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。

    题解:环形均分纸牌,只要做过均分纸牌,稍稍分析一下就可以得出结论。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, a[N], b[N], tot = 0, sum[N];
    ll ans, cnt;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read(), tot += a[i];
        tot /= n;
        rep(i, 1, n) b[i] = a[i]-tot;
        rep(i, 1, n) sum[i] = sum[i-1] + b[i];
    }
    void work(){
        nth_element(sum+1, sum+(n+1)/2, sum+n+1);
        cnt = sum[(n+1)/2];
        rep(i, 1, n) ans += abs(sum[i]-cnt);
    }
    void print(){
        printf("%lld
    ", ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

     HLOJ The Pilots Brothers refrigerator

    题意:给出4×4共16个门把手,改变一个门把手(打开或关闭)需要同时改变同行同列的门把手,当所有门把手都打开时才能打开门。+代表关,-代表开。

    题解:要使一个为'+'的符号变为'-',必须其相应的行和列的操作数为奇数;可以证明,如果'+'位置对应的行和列上每一个位置都进行一次操作,则整个图只有这一'+'位置的符号改变,其余都不会改变.

    将所有的行和列的位置都加1后,在将其模2之前,对给定的数组状态,将所有的位置操作其所存的操作数个次数,举例,如果a[i][j]==n,则对(i,j)操作n次,当所有的操作完后,即全为‘-’的数组。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    char s[10][10];
    int a[10][10], vis[10][10];
    int ansx[N], ansy[N], ans = 0, h = 0;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        rep(i, 1, 4) rep(j, 1, 4) cin >> s[i][j];
        rep(i, 1, 4) rep(j, 1, 4){
            if(s[i][j] == '+') {
                a[i][j] ^= 1;
                rep(k, 1, 4) a[i][k] ^= 1;
                rep(k, 1, 4) a[k][j] ^= 1; 
            }    
        }
        rep(i, 1, 4) rep(j, 1, 4){
            if(a[i][j]) {
                ans++;
                ansx[++h] = i;
                ansy[h] = j;
            }
        }
        printf("%d
    ", ans);
        rep(i, 1, h) printf("%d %d
    ", ansx[i], ansy[i]);
        return 0;
    }
    View Code

     HLOJ 占卜DIV

    题意(稍稍有点长):

    lyd学会了使用扑克DIY占卜。方法如下:一副去掉大小王的扑克共52张,打乱后均分为13堆,编号1~13,每堆4张,其中第13堆称作“生命牌”,也就是说你有4条命。这里边,4张K被称作死神。

     初始状态下,所有的牌背面朝上扣下。

     流程如下:

     1.抽取生命牌中的最上面一张(第一张)。

     2.把这张牌翻开,正面朝上,放到牌上的数字所对应编号的堆的最上边。(例如抽到2,正面朝上放到第2堆牌最上面,又比如抽到J,放到第11堆牌最上边,注意是正面朝上放)

     3.从刚放了牌的那一堆最底下(最后一张)抽取一张牌,重复第2步。(例如你上次抽了2,放到了第二堆顶部,现在抽第二堆最后一张发现是8,又放到第8堆顶部.........)

     4.在抽牌过程中如果抽到K,则称死了一条命,就扔掉K再从第1步开始。

     5.当发现四条命都死了以后,统计现在每堆牌上边正面朝上的牌的数目,只要同一数字的牌出现4张正面朝上的牌(比如4个A),则称“开了一对”,当然4个K是不算的。

     6.统计一共开了多少对,开了0对称作"极凶",1~2对为“大凶”,3对为“凶”,4~5对为“小凶”,6对为“中庸”,7~8对“小吉”,9对为“吉”,10~11为“大吉”,12为“满堂开花,极吉”。

    题解:清一色的模拟题,按照题意直接模拟,注意一下细节。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int s[14][5], k[14], sum[15];
    int ans = 0;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    
    int main(){
        rep(i, 1, 13) rep(j, 1, 4) {
            char ch;
            cin >> ch; 
            if(ch == 'A') s[i][j] = 1;
            else if(ch == '0') s[i][j] = 10;
            else if(ch == 'J') s[i][j] = 11;
            else if(ch == 'Q') s[i][j] = 12;
            else if(ch == 'K') s[i][j] = 13;
              else s[i][j] = (int)(ch-'0');
        }
          rep(i, 1, 13) k[i] = 4;
          rep(i, 1, 4){
            int x = s[13][i];
            while (x != 13) {
                sum[x]++;
                  x = s[x][k[x]--]; 
            }
          }
          rep(i, 1, 14) 
            if (sum[i] == 4) ans++;
        printf("%d
    ", ans);
        return 0;
    }
    View Code

     HLOJ solders

    题意:这个题目的意思是给你n个士兵在棋盘里的坐标,要你将他们排成连续的一行(即与x轴平行),问你最少要将这些士兵移动多少步。

    题解:y坐标取中位数,x坐标转换为右值相等即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, sum = 0, totx = 0, toty = 0;
    int x[N], y[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        n = read();
        rep(i, 1, n) x[i] = read(), y[i] = read();
        sort(y+1, y+n+1);
        toty = y[n/2+1];
        sort(x+1, x+n+1);
        rep(i, 1, n) x[i] -= (i-1);
        sort(x+1, x+n+1);
        totx = x[n/2+1];
        rep(i, 1, n) sum += abs(totx-x[i]) + abs(toty-y[i]);
        printf("%d
    ", sum);
        return 0;
    }
    View Code

    HLOJ Sumdiv

    题意:求A的B次方mod 1e9 + 7 的值。

    题解:对A进行质因子分解,然后推出计算公式,等比数列求和后,求个逆元,再累乘取模即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const ll mod = 1000000007;
    const int N = 1e6 + 50;
    ll a, b, p[N], c[N], k = 0;
    ll ans = 1;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void divide(ll x){
        rep(i, 2, sqrt(x)) 
            if(x % i == 0) {
                p[++k] = i; c[k] = 0;
                while(x % i == 0) x /= i, c[k]++;
            }
        if(x > 1) p[++k] = x, c[k] = 1;
    }
    ll pow(ll a, ll b){
        ll ans = 1;
        while(b){
            if(b & 1) ans = (ans*a)%mod;
            b >>= 1;
            a = a*a%mod;
        }
        return ans;
    }
    void work(){
        rep(i, 1, k){
            ans = ans*( ( pow( p[i], b*c[i]+1 )-1 )%mod * pow( p[i]-1, mod-2 )%mod)%mod;
        }
    }
    int main(){
        a = read(); b = read();
        divide(a);    
        work();
        if(a == 0) printf("0
    ");
        else printf("%lld
    ", (ans+mod)%mod);
        return 0;
    }
    View Code

     HLOJ 防线

    题意:用三个整数S,E 和D 来描述一组防具,即这一组防具布置在防线的S,S + D,S + 2D,...,S + KD(K∈Z,S + KD≤E,S + (K + 1)D>E)位置上。求某个具有奇数个防具的位置。

    题解:整体为二分的思想,二分坐标,l为0, r为最远的点,因为最多只有一个奇数,所以每次二分找区间的数字和是不是奇数即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    const ll inf = 9984432123;
    ll n, t;
    ll e[N],s[N],d[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    ll calc(ll x){
        ll res = 0;
        for(int i = 1;i <= n;i++)
            if(s[i] <= x) res = res+(min(e[i], x)-s[i])/d[i]+1;
        return res;
    }
    int main(){
        t = read();
        while(t--){
            n = read();
            ll l = 0, r;
            for(int i = 1;i <= n;i++) s[i] = read(), e[i] = read(), d[i] = read(), r = max(r, e[i]);
            ll ans = 0;
            while(l <= r){
                ll mid = (l+r)>>1;
                if(calc(mid) & 1) r = mid-1, ans = mid;
                else l = mid+1;
            }
            if(!ans) puts("There's no weakness.");
            else printf("%lld %lld
    ", ans, calc(ans)-calc(ans-1));
        }
        return 0;
    }
    View Code

     HLOJ to the max

    题意:给定一个n*n的矩阵,求其最大的子矩阵的数字和。(n <= 100)

    题解:大水题,预处理前缀和,直接枚举计算,取最大值即可。                                                                                                                                                                      

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, a[3010][3010];
    ll f[3010][3010], ans = 0;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) rep(j, 1, n) a[i][j] = read();
        f[1][1] = a[1][1];
        rep(i, 1, n){  
            f[i][1] = f[i-1][1] + a[i][1]; 
            rep(j, 2, n){
                f[i][j] = f[i-1][j] + f[i][j-1] + a[i][j] - f[i-1][j-1];
            }
        }
    }
    void work(){
        ans = a[1][1];
        rep(i, 1, n) rep(j, 1, n) rep(p, i, n) rep(q, j, n){
            ll sum = f[p][q] - f[i-1][q] - f[p][j-1] + f[i-1][j-1];
            ans = max(ans, sum);
        }
    }
    void print(){
        printf("%lld
    ", ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    HLOJ Sunscreen

    题意:

    奶牛们计划着去海滩上享受日光浴。为了避免皮肤被阳光灼伤,所有C(1 <= C <= 2500)头奶牛必须在出门之前在身上抹防晒霜。第i头奶牛适合的最小和最 大的SPF值分别为minSPF_i和maxSPF_i(1 <= minSPF_i <= 1,000; minSPF_i <= maxSPF_i <= 1,000)。如果某头奶牛涂的防晒霜的SPF值过小,那么阳光仍然能 把她的皮肤灼伤;如果防晒霜的SPF值过大,则会使日光浴与躺在屋里睡觉变得 几乎没有差别。为此,奶牛们准备了一大篮子防晒霜,一共L(1 <= L <= 2500)瓶。第i瓶 防晒霜的SPF值为SPF_i(1 <= SPF_i <= 1,000)。瓶子的大小也不一定相同,第i 瓶防晒霜可供cover_i头奶牛使用。当然,每头奶牛只能涂某一个瓶子里的防晒霜 ,而不能把若干个瓶里的混合着用。 请你计算一下,如果使用奶牛们准备的防晒霜,最多有多少奶牛能在不被灼 伤的前提下,享受到日光浴的效果?

    简明一点就是:给你若干线段和点,每个点可以与包含这个点的线段匹配,求最大匹配数。

    题解:线段按右端点排序,防晒霜按防护值排序,之后进行贪心即可,注意防晒霜的数量。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int k, c, ans = 0;
    struct cow{ int l, r; } a[N];
    struct node{ int l, r; } b[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    bool cmp1(cow x, cow y){return x.l > y.l;}
    bool cmp2(node x, node y){return x.l > y.l;}
    int main(){
        c = read(); k = read();
        rep(i, 1, c) a[i].l = read(), a[i].r = read();
        rep(i, 1, k) b[i].l = read(), b[i].r = read();
        sort(a+1, a+c+1, cmp1);
        sort(b+1, b+k+1, cmp2);
        rep(i, 1, c) rep(j, 1, k){
            if(b[j].l >= a[i].l && b[j].l <= a[i].r && b[j].r) {
                ans++; b[j].r--;
                break; 
            }
        }
        printf("%d
    ", ans);
        return 0; 
    }
    View Code

    HLOJ Task

    题意:有n个机器,m个任务。每个机器至多能完成一个任务。对于每个机器,有一个最大运行时间xi和等级yi,对于每个任务,也有一个运行时间xj和等级yj。只有当xi>=xj且yi>=yj的时候,机器i才能完成任务j,并获得500xj+2yj金钱。问最多能完成几个任务,当出现多种情况时,输出获得金钱最多的情况。

    题解:根据任务去选择机器,任务从小到大排列好,遍历的时候。每次先把所有时间大于该任务的机器按照等级存储下来,同等级的机器是等效的,因为只要能完成任务都是等效的,然后取其中恰好大于等于该任务的等级的机器,正确性可以证明。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, m, maxx = 0, ans1, p[N];
    ll ans2;
    struct node{ int x, y; } a[N];
    struct task{ int x, y;} b[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    bool mycmp1(node x, node y){ return (x.x > y.x) || (x.x == y.x && x.y > y.y ); }
    bool mycmp2(task x, task y){ return (x.x > y.x) || (x.x == y.x && x.y > y.y ); }
    void init(){
        n = read(); m = read();
        rep(i, 1, n) a[i].x = read(), a[i].y = read();
        rep(i, 1, m) b[i].x = read(), b[i].y = read();
    }
    void work(){
        sort(a+1, a+n+1, mycmp1);
        sort(b+1, b+m+1, mycmp2);
        int j = 1;
        rep(i, 1, m){
            while(j <= n && a[j].x >= b[i].x){
                p[a[j].y] ++;
                j++;
            }
            rep(k, b[i].y, 100){
                if(p[k]){
                    p[k] --;
                    ans1++; ans2 += (ll)b[i].x*500 + b[i].y * 2;
                    break;
                }
            }
        }
    }
    void print(){
        printf("%d %lld
    ", ans1, ans2);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    9月16日

    HLOJ 货仓选址

    题意:在一条数轴上有N家商店,它们的坐标分别为 A[1]~A[N]。现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

    题解:大水题,先按坐标排序,取中位数为货仓地址,直接计算即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, a[N], ans = 0, pos;  
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read();
        sort(a+1, a+n+1); 
    }
    void work(){
        pos = a[n/2+1];
        rep(i, 1, n) ans += (ll)abs(a[i] - pos);
    }
    void print(){
        printf("%lld
    " , ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    HLOJ Corral the Cows

    题意:约翰打算建一个围栏来圈养他的奶牛.作为最挑剔的兽类,奶牛们要求这个围栏必须是正方形的,而且围栏里至少要有(1<=C< =500)个草场,来供应她们的午餐. 约翰的土地上共有C<=N<=500)个草场,每个草场在一块1x1的方格内,而且这个方格的 坐标不会超过10000.有时候,会有多个草场在同一个方格内,那他们的坐标就会相同. 告诉约翰,最小的围栏的边长是多少?

    题解:考虑将坐标离散化,然后求二维前缀和,由于N<=500,所以离散化后最多也只有1000个点,检验时,我们枚举正方形的左上角,用二分求出它的右下角,然后,判断正方形内是否有大于C的草量。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    const int str = 10000;
    struct node{ int x, y; } a[N];
    int f[3010][3010];
    int n, c, minn = N, maxx = 0, ans = 0; 
    int px[N], py[N];
    int bx[N], by[N], xc, yc;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    bool mycmp(node a, node b){ return a.x < b.x; }
    void init(){
        c = read(), n = read();
        rep(i, 1, n) a[i].x = read(), a[i].y = read(), px[a[i].x]++, py[a[i].y]++;
        rep(i, 1, str) {
            if(px[i]) bx[++xc] = i;
            px[i] = xc;
            if(py[i]) by[++yc] = i;
             py[i] = yc;
        }
        rep(i, 1, n) f[px[a[i].x]][py[a[i].y]]++;
        rep(i, 1, xc) rep(j, 1, yc){
            f[i][j] += f[i-1][j] + f[i][j-1] - f[i-1][j-1];
        }
    }
    bool check(int x){
        rep(i, px[x], xc) rep(j, py[x], yc){
            int k = 0, l = 0;
            if (bx[i] - x >= 0) k = px[bx[i] - x];
            if (by[j] - x >= 0) l = py[by[j] - x];
            if (f[i][j] - f[k][j] - f[i][l] + f[k][l] >= c)
                return 1;
        }
        return 0;
    }
    void work(){
        int l = 1, r = str;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (check(mid)) r = mid;
            else l = mid + 1;
        }
        ans = l;
    }
    void print(){
        printf("%d
    ", ans);
    } 
    int main(){
        init();
        work();
        print(); 
        return 0;
    }
    View Code

    HLOJ Stall Reservation

    题意:给出n个区间,第i个区间表示为[li,ri],询问把这些最少的分组数,使得每组内每个区间相离,1<=N<=50,000

    题解:按左端点排序

    现在来考虑第i个区间的决策点,对于能分进的组j,必然是aj<lj,第i个区间才能被分进去,但是选哪一个组,按照朴素的思想,必然是选aj最小的或者最大的,因为是按左端点排序,后面的区间左端点必然增大,于是前面区间能分进的组,后面的区间也能分进,于是无论选哪一个组都无所谓,如果不能选,必然要新开一个组。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    struct cow{ int l, r, id; } a[N];
    int n, k = 0, f[N];
    priority_queue < pii, vector< pii >, greater< pii > > q;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    bool mycmp(cow a, cow b){ return (a.l < b.l || (a.l == b.l && a.r < b.r)); }
    void init(){
        n = read();
        rep(i, 1, n) a[i].l = read(), a[i].r = read(), a[i].id = i;
    } 
    void work(){
        sort(a+1, a+n+1, mycmp);
        q.push(mp(a[1].r, ++k));
        f[a[1].id] = k;
        rep(i, 2, n) {
            if(a[i].l > q.top().first) {
                int x = q.top().second;
                q.pop();
                f[a[i].id] = x;
                q.push(mp(a[i].r, x));
            }
            else {
                f[a[i].id] = ++k;
                q.push(mp(a[i].r, k));
            }
        }
    }
    void print(){
        printf("%d
    ", k);
        rep(i, 1, n) printf("%d
    ", f[i]);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    HLOJ Ultra-QuickSort

    题意:给定一个序列,求这个序列的逆序对数。

    题解:模板题,直接树状数组求逆序对就莫得了。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, a[N], c[N];
    ll lowbit(ll x){ return x&(-x);}
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void add(ll k, ll v){ while(k <= N) c[k] += v, k += lowbit(k);  }
    ll sum(ll k){
        ll ans = 0;
        while(k) ans += c[k], k -= lowbit(k);
        return ans;
    }
    void init(){
        while(~scanf("%lld", &n)){
            if(!n) break;
            ll ans = 0;
            memset(c, 0, sizeof(c));
            rep(i, 1, n) a[i] = read();    
            per(i, n, 1){
                if(a[i] == 0) {
                    ans += (ll)i-1;
                    continue;
                }
                ans += (ll)sum(a[i]-1);
                add(a[i], 1); 
            }    
            printf("%lld
    ", ans);
        }
    }
    int main(){
        init();
        return 0;
    }
    View Code

    HLOJ 奇数码问题

    题意:在一个n*n的网格中进行,其中n为奇数,1个空格和1~n*n-1这n*n-1个数恰好不重不漏地分布在n*n的网格中。空格移动的规则与八数码游戏相同,实际上,八数码就是一个n=3的奇数码游戏。现在给定两个奇数码游戏的局面,请判断是否存在一种移动空格的方式,使得其中一个局面可以变化到另一个局面。

    题解:当此表达式成立时,状态可达:(状态1奇偶性==状态2奇偶性)==(空格距离%2==0)。若两个状态的可相互到达,则有,两个状态的逆序奇偶性相同且空格距离为偶数,或者,逆序奇偶性不同且空格距离为奇数数。否则不能。逆序对用树状数组求。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, a[N], c[N];
    int lowbit(int x){ return x&(-x);}
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void add(int k, int v){ while(k <= n) c[k] += v, k += lowbit(k);  }
    int sum(int k){
        int ans = 0;
        while(k) ans += c[k], k -= lowbit(k);
        return ans;
    }
    int work(){
        memset(c, 0, sizeof(c));
        int ans = 0;
        rep(i, 1, n){
            a[i] = read();
            if(!a[i]) continue;
            ans += sum(n) - sum(a[i]);
            add(a[i], 1);
        }
        return ans&1;
    }
    void init(){
        while(~scanf("%d", &n)){
            n *= n;
            if(work() == work()) printf("TAK
    ");
            else printf("NTE
    ");
        }
    }
    int main(){
        init();
        return 0;
    }
    View Code

    HLOJ Running Median

    题意:你需要写一个程序,读入一个整数序列(在int范围内),每读入奇数个数后输出当前的中位数。

    题解:考虑用两个堆来维护,一个大根堆一个小根堆。每次比较两个堆的堆顶,如果不相等就交换堆顶,否则堆顶即为所要求的中位数。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int t, n, m, k;
    priority_queue < int, vector<int>, greater<int> > x;
    priority_queue < int, vector<int>, less<int> > y;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        t = read();
        while(t--){
            m = read(); n = read();
            printf("%d %d
    ", m, n/2+1);
            k = read();
            while(x.size()) x.pop();
            while(y.size()) y.pop();
            printf("%d ", k);
            y.push(k);
            rep(i, 2, n){
                k = read();
                if(k > y.top()) x.push(k);
                else y.push(k);
                while(abs(x.size()-y.size()) > 1){
                    if(x.size() > y.size()){
                        y.push(x.top());
                        x.pop();
                    }
                    else {
                        x.push(y.top());
                        y.pop();
                    }
                }
                if(i & 1){
                    if(y.size() > x.size()) printf("%d ", y.top());
                    else printf("%d ", x.top());
                }
                if(i % 20 == 19) printf("
    ");
            }
            printf("
    ");
        }    
        return 0;
    }
    View Code

    HLOJ 七夕祭

    题意:有一个会场由N排M列共计N×M个摊点组成。但是小LL只对其中的一部分摊点感兴趣。他预先联系了会场的负责人,希望能够通过恰当地布置会场,使得各行中他感兴趣的摊点数一样多,并且各列中他感兴趣的摊点数也一样多。但是摊点已经随意布置完毕了,如果想满足小LL的要求,唯一的调整方式就是交换两个相邻的摊点。两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上,每一行或每一列的第一个位置和最后一个位置也算作相邻。现在小LL想知道他的两个要求最多能满足多少个。在此前提下,至少需要交换多少次摊点。

    题解:类似糖果传递,设 bi 的前缀和为 si。如果从第 k 个位置开始,那么第 i 堆和第 i+1 堆交换的纸牌数就是 |si-sk|。总代价就是|s1-sk|+|s2-sk|+|s3-sk|+……+|sn-sk|。发现什么了?当 sk 是 s1~sn 中位数的时候,上式有最小值!所以把 si 排序后,令 sk=s[(n+1)/2],计算代价即可。时间复杂度 O(nlogn),预计得分 100 分。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, m, t, a[N], b[N];
    ll ans = 0;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read(); m = read(); t = read();
        rep(i, 1, t){
            int x = read(), y = read();
            a[x]++, b[y]++; 
        }
        if((t % n != 0) && (t % m != 0)){
            puts("impossible");
            exit(0);
        }
    }
    void work(){
        rep(i, 1, n) a[i] -= t/n;
        rep(i, 1, m) b[i] -= t/m;
        if(t % n == 0){
            rep(i, 2, n) a[i] += a[i-1];
            sort(a+1, a+n+1);
            rep(i, 1, n) ans += (ll)abs(a[i] - a[(n+1)/2]);
        }
        if(t % m == 0){
            rep(i, 2, m) b[i] += b[i-1];
            sort(b+1, b+m+1);
            rep(i, 1, m) ans += (ll)abs(b[i]-b[(m+1)/2]);
        }
    }
    void print(){
        if(t % n != 0) printf("column ");
        else {
            if(t % m != 0) printf("row ");
            else printf("both ");
        }
        printf("%lld
    ", ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    9月17日

    HLOJ Color a Tree

    题意:有一棵树,每个节点都有一个代价基值Ci。现在要给每个点染色,第一个染根节点,其余的节点染色的时候其父节点必须已染色。每个节点染色会用掉一个时间单位,每个节点染色的代价是染完此节点时的总时间T*Ci。问染完全部节点所需要的最小代价。

    题解:每次找到一个权值最大的节点,如果它是根节点,则首先对它染色,否则的话我们可以得出一个结论,在对它的父亲已经染色的情况下,立刻给它染色是最优的。现在重点讨论第二种情况,当它不是根节点时,我们如果对它父亲染了色,则一定会立刻对它染色,所以可以把它和它父亲合并为同一个节点,它和它父亲的儿子都成为了新节点的儿子,它的父亲的父亲则是新节点的父亲。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, root, a[N], ans;
    int head[N], cnt = 0;
    struct node{ 
        int to, next, fa, t;
        double v; 
    } e[N];
    void add(int x, int y){
        cnt ++; 
        e[cnt].to = y;
        e[cnt].next = head[x];
        head[x] = cnt;
    }
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int get(){
        int res = -1;
        double sum = 0;
        rep(i, 1, n) {
            if(i != root && sum < e[i].v){
                sum = e[i].v;
                res = i;
            }
        }
        return res;
    }
    void init(){
        n = read(); root = read(); 
        rep(i, 1, n) a[i] = read(), e[i].v = a[i], ans += a[i], e[i].t = 1;
        rep(i, 1, n-1){
            int xx, yy;
            xx = read(); yy = read();
            add(xx, yy);
            e[yy].fa = xx;
        }
    }
    void work(){
        rep(i, 1, n-1){
            int u = get();
            ans += a[u] * e[e[u].fa].t;
            e[u].v = -1;
            rep(j, 1, n) if (e[j].fa == u) e[j].fa = e[u].fa;
            a[e[u].fa] += a[u];
            e[e[u].fa].t += e[u].t;
            e[e[u].fa].v = (double) a[e[u].fa] / e[e[u].fa].t;
        }    
    }
    void print(){
        printf("%d
    ", ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    HLOJ 兔子与兔子

    题意:很久很久以前,森林里住着一群兔子。有一天,兔子们想要研究自己的 DNA 序列。我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母),然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。

    题解:Hash表模板题,用Hash表存储前缀DNA序列。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    char s[N];
    int n, q;
    unsigned long long f[N], p[N]; 
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        scanf("%s", s+1); q = read();
        n = strlen(s+1); 
        p[0] = 1;
        rep(i, 1, n) {
            f[i] = f[i-1] * 131 + (s[i]-'a'+1);
            p[i] = p[i-1] * 131;
        }
    }
    void work(){
        rep(i, 1, q){
            int l1, l2, r1, r2;
            l1 = read(); r1 = read(); l2 = read(); r2 = read();
            if(f[r1] - f[l1-1] * p[r1-l1+1] == f[r2] - f[l2-1] * p[r2-l2+1]){
                puts("Yes");
            }
            else puts("No");
        }
    }
    int main(){
        init();
        work();
        return 0;
    }
    View Code

    HLOJ 最大子序和

    题意:输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。

    题解:对序列进行单调队列维护即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, m, a[N], sum[N];
    ll q[N], ans = 0;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read(); m = read();
        rep(i, 1, n) a[i] = read(), sum[i] = sum[i-1] + a[i];
    }
    void work(){
        int l = 1, r = 1;
        q[1] = 0;
        rep(i, 1, n) {
            while(l <= r && q[l] < i - m) l++;
            ans = max(ans, sum[i] - sum[q[l]]);
            while(l <= r && sum[q[r]] >= sum[i]) r--;
            q[++r] = i;
        }
    }
    void print(){
        printf("%lld
    ", ans);
    }
    int main(){
        init(); 
        work();
        print();
        return 0;
    }
    View Code

    HLOJ 无穷的序列

    题意:有一个无穷序列如下:110100100010000100000… 请你找出这个无穷序列中指定位置上的数字。

    题解:bitset模板题,对序列用bitset维护。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e7 + 50;
    int n, a[N], maxn = 0, k;
    bitset <100000000> p;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read(), maxn = max(maxn, a[i]);
        for(int i = 1;i <= maxn; i += k, k++) p.set(i);
    }
    void print(){
        rep(i, 1, n) printf("%d
    ", p.test(a[i]));
    }
    int main(){
        init(); 
        print();
        return 0;
    }
    View Code

    HLOJ Largest Rectangle in a Histogram

    题意:这道题让求直方图中最大的矩形(具体略)。

    题解:单调栈模板题,对矩形面积用单调栈维护。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, a[N], s[N], w[N], p = 0;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        while(~scanf("%d", &n)){
            if(n == 0) break; 
            ll ans = 0;
            rep(i, 1, n) a[i] = read();
            a[n+1] = p = 0;
            rep(i, 1, n+1){
                if(a[i] > s[p]) s[++p] = a[i], w[p] = 1;
                else {
                    int width = 0;
                    while (s[p] > a[i]) {
                        width += w[p];
                        ans = max(ans, (ll)width * s[p]);
                        p--;
                    }
                    s[++p] = a[i], w[p] = width + 1;
                }
            }
            printf("%lld
    ", ans);
        }
    }
    int main(){
        init();
        return 0;
    }
    View Code

    HLOJ Team Queue

    题意:有t个团队的人正在排一个长队。每次新来一个人时,如果他有队友在排队,那么这个新人会插队到最后一个队友的身后。如果没有任何一个队友排队,则他会排到长队的队尾。 输入每个团队中所有队员的编号,要求支持如下3种指令(前两种指令可以穿插进行) 对于每个DEQUEUE指令,输出出队人的编号。

    题解:队列维护序列,按照题意描述操作序列就行了。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e4 + 50;
    int n, m, v[N*N], p, t;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int main(){
        while(~scanf("%d", &n)){
            if(n == 0) break;
            t++; queue <int> q[N];
            printf("Scenario #%d
    ", t);
            rep(i, 1, n){
                m = read();
                rep(j, 1, m) p = read(), v[p] = i;
            }
            char ch[100]; int x;
            while(~scanf("%s", ch)){
                if(ch[0] == 'S') break;
                if(ch[0] == 'E') {
                    x = read();
                    if(q[v[x]].empty()) q[0].push(v[x]);
                    q[v[x]].push(x);
                }
                if(ch[0] == 'D'){
                    printf("%d
    ",q[q[0].front()].front());
                    q[q[0].front()].pop();
                    if(q[q[0].front()].empty()) q[0].pop();
                }
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

    HLOJ Raid*

    题意:给定两组点的坐标,求不在同一组内的点的最小距离。

    题解:平面最近点对问题,将同一组内的点的距离赋值为正无穷,分治法求一下平面最近点对即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50, inf = 0x3f3f3f3f;
    struct node{ int x, y, id; } a[N], q[N];
    int n, t;
    double ans; 
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    double dist(node a, node b){ 
        if(a.id == b.id) return 1e9;
        return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
    }
    double MIN(double a, double b){ return a < b ? a : b;}
    bool mycmp1(node a, node b){ return a.x < b.x; }
    bool mycmp2(node a, node b){ return a.y < b.y; }
    double divide(int l, int r){
        if(l == r) return 1e9;
        int mid = (l+r)>>1, k = 0;
        double ans = MIN(divide(l, mid), divide(mid+1, r));
        rep(i, l, r) if(fabs(a[i].x - a[mid].x) <= ans) q[++k] = a[i];
        sort(q+1, q+k+1, mycmp2);
        rep(i, 1, k) rep(j, i+1, k){
            if(q[j].y - q[i].y <= ans) ans = MIN(ans, dist(q[i], q[j]));
        }    
        return ans;
    }
    int main(){
        t = read();
        while(t --> 0){
            n = read();
            rep(i, 1, (n<<1)){
                a[i].x = read(), a[i].y = read();
                a[i].id = (i <= n)?0:1;
            }
            sort(a+1, a+(n<<1)+1, mycmp1);
            ans = divide(1, (n<<1));
            printf("%.3lf
    ", ans);
        }
        return 0;
    }
    View Code

    HLOJ Snowflake Snow Snowflakes

    题意:您可能听说没有两个雪花是相似的。 你的任务是编写一个程序来确定这是否真的如此。 您的程序将读取有关雪花集合的信息,并搜索可能相同的一对。 每个雪花都有六个手臂。 对于每个雪花,您的程序将提供六个臂中每个臂的长度的测量。 任何具有相同长度相应臂的雪花都应该被程序标记为可能相同。

    题解:在读入一个雪花的时候把这些情况全部放入哈希表中,如果某次插入的时候发生冲突,则说明存在重复的雪花,并且后面的不需要再处理。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e5 + 50;
    int n, tot, mod = 99991, snow[N][6], head[N], next[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    int H(int *a){
        int sum = 0, mul = 1;
        rep(i, 0, 5){
            sum = (sum + a[i]) % mod;
            mul = (ll)mul * a[i] % mod;
        }
        return (sum + mul) % mod;
    }
    bool equal(int *a, int *b){
        rep(i, 0, 5) rep(j, 0, 5){
            bool eq = 1;
            rep(k, 0, 5) if(a[(i+k)%6] != b[(j+k)%6]) eq = 0;
            if(eq) return 1;
            eq = 1;
            rep(k, 0, 5) if(a[(i+k)%6] != b[(j-k+6)%6]) eq = 0;
            if(eq) return 1;
        }
        return 0;
    }
    bool insert(int *a){
        int val = H(a);
        for(int i = head[val];i;i = next[i]){
            if(equal(snow[i], a)) return 1;
        }
        ++tot;
        memcpy(snow[tot], a, 6 * sizeof(int));
        next[tot] = head[val];
        head[val] = tot;
        return 0;
    }
    int main(){
        n = read();
        rep(i, 1, n){
            int a[10];
            rep(j, 0, 5) a[j] = read();
            if(insert(a)){
                puts("Twin snowflakes found.");
                return 0;
            }
        }
        puts("No two snowflakes are alike.");
        return 0;
    }
    View Code

    9月18日

    HLOJ 前缀统计

    题意:给定N个字符串S1,S2...SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。输入字符串的总长度不超过10^6,仅包含小写字母。

    题解:Trie树模板题,对于每个询问,在检索过程中累加途径的每个节点的cnt值,就是该询问的答案。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, m, end[N];
    int trie[N][26], tot = 1;
    char s[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void insert(char* str){
        int len = strlen(str), p = 1;
        rep(k, 0, len-1){
            int ch = str[k] - 'a';
            if(trie[p][ch] == 0) trie[p][ch] = ++tot;
            p = trie[p][ch];
        }
        end[p]++;
    }
    int search(char* str){
        int len = strlen(str), p = 1, res = 0;
        rep(k, 0, len-1){
            int ch = s[k] - 'a';
            if (!trie[p][ch]) break;
            p = trie[p][ch];
            res += end[p];
        }
        return res;
    }
    int main(){
        n = read(); m = read();
        rep(i, 1, n)  scanf("%s", s), insert(s);
        rep(i, 1, m) {
            scanf("%s", s);
            printf("%d
    ", search(s));
        }
        return 0;
    }
    View Code

    HLOJ The XOR Largest Pair

    题意:在给定的N个整数A1,A2……AN中选出两个进行xor运算,得到的结果最大是多少?

    题解:把每个整数看作长度为32的二进制01串(数值较小时在前边补0),并且把A1~Ai-1对应的32位二进制串插入一棵Trie树即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int a[N];
    int trie[N*3][2], tot = 0;
    int n, ans = 0;
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void insert(int x){
        int p = 0;
        per(k, 31, 0){
            int ch = (x>>k) & 1;
            if(!trie[p][ch]) trie[p][ch] = ++tot;
            p = trie[p][ch]; 
        }
    }
    int search(int x){
        int p = 0, res = 0;
        per(k, 31, 0){
            int ch = (x>>k) & 1;
            if(trie[p][ch^1]) p = trie[p][ch^1], res = (res<<1) | 1;
            else p = trie[p][ch], res = (res<<1);
        }
        return res;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read(), insert(a[i]);
        rep(i, 1, n) ans = max(ans, search(a[i]));
    }
    void print(){
        printf("%d
    ", ans);
    }
    int main(){
        init();
        print();
        return 0;
    }
    View Code

     HLOJ 子序列累加和

    题意:现在有N个数的数列。现在你定义一个子序列是数列的连续一部分,子序列的值是这个子序列中最大值和最小值之差。给你这N个数,小x想知道所有子序列的值得累加和是多少

    题解:这道题分治应该可以做,不过要学习的是这道题的单调栈做法,就是求出序列的所有max值之和,再求出所有的min值之和,再相减,用单调栈实现序列中每一个数的贡献。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, a[N], s[N], maxl[N], minl[N], maxr[N], minr[N], k = 0;
    ll ans = 0; 
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read();
    }
    void work(){
        rep(i, 1, n){
            while(k != 0 && a[s[k]] < a[i]){
                maxl[i] += maxl[s[k]] + 1;
                k--; 
            } 
            s[++k] = i;
        }
        k = 0;
        per(i, n, 1){
            while(k != 0 && a[s[k]] <= a[i]){
                maxr[i] += maxr[s[k]] + 1;
                k--; 
            }
            s[++k] = i;
        }
        k = 0;
        rep(i, 1, n){
            while (k != 0 && a[s[k]] > a[i]){
                minl[i] += minl[s[k]] + 1;
                k--;
            }
            s[++k] = i;
        }
        k = 0;
        per(i, n, 1){
            while (k != 0 && a[s[k]] >= a[i]){
                minr[i] += minr[s[k]] + 1;
                k--;
            }
            s[++k] = i;
        }
        rep(i, 1, n)
            ans += ((maxl[i]+1)*(maxr[i]+1) - (minl[i]+1)*(minr[i]+1))*a[i];
    }
    void print(){
        printf("%lld
    ", ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    HLOJ holiday

    题意:经过几个月辛勤的工作,FJ决定让奶牛放假。假期可以在1…N天内任意选择一段(需要连续),每一天都有一个享受指数W。但是奶牛的要求非常苛刻,假期不能短于P天,否则奶牛不能得到足够的休息;假期也不能超过Q天,否则奶牛会玩的腻烦。FJ想知道奶牛们能获得的最大享受指数。

    题解:单调队列维护符合条件的序列的单调性。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    ll n, P, Q, a[N], sum[N];
    ll q[N], ans = -1e9;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read(), P = read(), Q = read();
        rep(i, 1, n) a[i] = read();
        rep(i, 1, n) sum[i] = sum[i-1] + a[i];
    }
    void work(){
        int l = 1, r = 0;
        rep(i, P, n){
            while(l <= r && sum[q[r]] >= sum[i-P]) r--;
            q[++r] = i-P;
            while(l <= r && q[l] < i-Q) l++;
            ans = max(ans, sum[i] - sum[q[l]]);
        }
    } 
    void print(){
        printf("%lld
    ", ans);
    }
    int main(){
        init();
        work(); 
        print();
        return 0;
    }
    View Code

    9月19日

    P3132 [USACO16JAN]愤怒的奶牛Angry Cows

    题意:有N个草堆在数轴的不同位置,坐标为x1,x2,….,xn。如果玩家以能量R把奶牛发射到坐标x,就会引爆半径R及以内的的草堆,即坐标范围[x−R,x+R]的草堆都会燃爆,每个被奶牛引爆的草堆又会2次引爆半径R-1及以内的的草堆,2次引爆的草堆又会3次引爆半径R-2及以内的的草堆...直到一次引爆后没有其他草堆被波及或半径为0。

    现在只有1头奶牛,能量为R,请计算如果要引爆所有的草堆,最小的R是多少?

    题解:用f[i]记录以i为以i为圆心可以向左覆盖前i-1个点的最小半径。 再用g[i]记录以i为圆心可以向右覆盖至第n个点的最小半径,二分答案即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a);i <= (b);i++)
    #define per(i, a, b) for(int i = (a);i >= (b);i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    double eps = 0.001;
    double a[N], s[N], maxx = 0, pos, posl, posr;
    double vis[N], f[N], g[N];
    double ans;
    int n;
    inline ll read(){
        ll x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read();
        sort(a+1, a+n+1);
        rep(i, 2, n) s[i] = a[i] - a[i-1];
        f[1] = g[n] = 0;
           rep(i, 2, n){
            f[i] = 1e9;
            int l = 1, r = i;
            while(l + 1 < r){
                int mid = (l+r)/2;
                if(f[mid-1] + 1 < a[i]-a[mid-1]) l = mid;
                else r = mid;
            }
            f[i] = min(max(a[i]-a[l-1], f[l-1] + 1), max(a[i] - a[r-1], f[r-1] + 1));
        }
        per(i, n-1, 1){
            g[i] = 1e9;
            int l = i, r = n - 1;
            while(l + 1 < r){
                int mid = (l+r)/2;
                if(g[mid+1] + 1 < a[mid+1] - a[i]) r = mid;
                else l = mid;
            }
            g[i] = min(max(a[l+1] - a[i], g[l+1] + 1), max(a[r+1] - a[i], g[r+1] + 1));
        }
    }
    bool check(double x){ 
        per(i, n, 1){
            if(f[i] + 1 <= x){
                for(int j = i;j <= n && a[j] <= a[i] + 2.0*x; j++)
                    if(g[j] + 1 <= x) return true;
                break;
            }
        }
        return false;
    }
    void work(){
        double l = 1.0, r = a[n]*1.0;
        while(eps < r-l){
            double mid = (l+r) / 2;
            if(check(mid)) r = mid;
            else l = mid;
        }
        ans = l;
    }
    void print(){
        printf("%.1lf", ans);
    }
    int main(){
        init();
        work();
        print();
        return 0;
    }
    View Code

    9月23日

    HLOJ Dropping Test

    题意:给定你n组ai,bi,让你取出n−k组,使得这nk组的a之和除以b之和最大.

    题解:01分数规划模板题,设答案为x, 则 a[i] - b[i] * x 的前n-k组的累加和必须大于等于0。二分答案即可。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const double eps = 1e-9;
    const int N = 1e6 + 50;
    int n, k;
    double a[N], b[N], v[N];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    bool mycmp(double x, double y) {return x > y; }
    bool check(double x){
        double res = 0;
        rep(i, 1, n) v[i] = a[i] - b[i] * x;
        sort(v+1, v+n+1, mycmp); 
        rep(i, 1, n-k) res += v[i];
        return res >= 0;
    }
    void init(){
        while(~scanf("%d%d", &n, &k)) {
            if(n == 0) break;
            double l = 0, r = 0;
            rep(i, 1, n) scanf("%lf", &a[i]), r += a[i];
            rep(i, 1, n) scanf("%lf", &b[i]);
            while(r-l > eps){
                double mid = (l+r) / 2;
                if(check(mid)) l = mid;
                else r = mid;
            }
            printf("%.0lf
    ", l * 100);    
        }
    }
    int main(){
        init();
        return 0;
    }
    View Code

    9月24日

    HLOJ 花店橱窗

    题意:https://www.luogu.org/problem/P1854 

    题解:一道比较显然的DP,设F(i,j)表示到(i,j)位置的最优值,状态转移也非常显然,不再叙述,主要是输出方案值得学习,看代码理解。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    int n, m, a[2010][2010], f[2010][2010];
    int ans = -1e9, k[N]; 
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init() {
        n = read(); m = read();
        rep(i, 1, n) rep(j, 1, m) a[i][j] = read(), f[i][j] = -1e9;
    }
    void find(int x, int y){
        if(x <= 0) return ;
        int p = x;
        while(f[x][p] != y) p++;
        k[x] = p;
        find(x-1, y-a[x][p]);
        return ;
    }
    void work() {
        rep(i, 1, n) rep(j, 1, m) rep(k, i-1, j-1){
            f[i][j] = max(f[i][j], f[i-1][k] + a[i][j]);
        }
        rep(i, n, m) ans = max(ans, f[n][i]);
        printf("%d
    ", ans);
        find(n, ans);
        rep(i, 1, n) printf("%d ", k[i]);
        printf("
    ");
    }
    int main(){
        init();
        work();
        return 0;
    }
    View Code

     hnoi2010 合唱队

    题意:给定一串序列,问有多少种初始序列经过如题操作可以得到此序列。

    题解:f[i][j][1]为可以排成理想队列中[i,j][区间,且以最后一个排进去是第i人的初始队列种数。

    f[i][j][0]为可以排成理想队列中[i,j]区间,且以最后一个排进去是第j人的初始队列种数。

    限于篇幅,状态转移部分看代码理解。

    #include<bits/stdc++.h>
    
    #define ll long long
    #define mp make_pair
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    
    using namespace std;
    
    typedef pair<int, int> pii;
    typedef double db;
    const int N = 1e6 + 50;
    const int mod = 19650827;
    int n, a[N], f[1010][1010][2];
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
        while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
        return x*f;
    }
    void init(){
        n = read();
        rep(i, 1, n) a[i] = read();
        rep(i, 1, n) f[i][i][1] = 1;
        per(l, n, 1) rep(r, l+1, n) {
            f[l][r][0] = (f[l][r][0] + f[l+1][r][0] * (a[l] < a[l+1]))%mod;
            f[l][r][0] = (f[l][r][0] + f[l+1][r][1] * (a[l] < a[r]))%mod; 
            f[l][r][1] = (f[l][r][1] + f[l][r-1][0] * (a[r] > a[l]))%mod;
            f[l][r][1] = (f[l][r][1] + f[l][r-1][1] * (a[r] > a[r-1]))%mod;
        }
        printf("%d
    ", (f[1][n][1] + f[1][n][0])%mod);
    }
    int main(){
        init();
        return 0;
    }
    View Code
  • 相关阅读:
    ISO/IEC 9899:2011 条款6.9.1——函数定义
    ISO/IEC 9899:2011 条款6.9——外部定义
    ISO/IEC 9899:2011 条款6.8.6——跳转语句
    ISO/IEC 9899:2011 条款6.8.5——迭代语句
    Objective-C轻量级泛型
    ISO/IEC 9899:2011 条款6.8.4——选择语句
    ISO/IEC 9899:2011 条款6.8.3——表达式与空语句
    ISO/IEC 9899:2011 条款6.8.2——标签语句
    ISO/IEC 9899:2011 条款6.8.1——标签语句
    ISO/IEC 9899:2011 条款6.8——语句和语句块
  • 原文地址:https://www.cnblogs.com/smilke/p/11523009.html
Copyright © 2011-2022 走看看