平时十八测

今天没有一道题想到正解,全部打的暴力,今天题思维很活啊;

第一题:主要是利用只有26个字母,往这方面想了,但失败了;

计数排序:主要针对数据很集中的;

建26棵线段树,记录区间中该数有多少个,每次查询区间中26个字母各有多少个,按顺序排列;就一个区间查询,修改操作,此题卡常;

#include<bits/stdc++.h>
using namespace std;

const int M = 1e5 + 5;
char s[M];
int f[M][27], n, m, cnt[27];
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
#define For(a, b, c) for(int a = b; a <= c; a++)
struct Node{
    int v, tag; 
    Node *ls, *rs;
    void up(){
        v = ls->v + rs->v;
    }
    void down(int lf, int rg){
        if(tag != -1){
            int mid = (lf + rg) >> 1;
            ls->tag = rs->tag = tag;
            ls->v = (mid - lf + 1) * tag;
            rs->v = (rg - mid) * tag;
            tag = -1;
        }
    }
    
}pool[(M * 27 )<< 2], *root[27], *tail = pool;
#define Ls nd->ls, lf, mid
#define Rs nd->rs, mid+1, rg 
Node *build(int d, int lf = 1, int rg = n){
    Node *nd = ++tail;
    nd->tag = -1;
    if(lf == rg)nd->v = f[lf][d];
    else {
        int mid = (lf + rg) >> 1;
        nd->ls = build(d, lf, mid);
        nd->rs = build(d, mid+1, rg);
        nd->up();
    }
    return nd;
}
void del(int L, int R, Node *nd, int lf = 1, int rg = n){
    if(L <= lf && rg <= R){
        nd->v = rg - lf + 1;
        nd->tag = 1;
    }
    else {
        nd->down(lf, rg);
        int mid = (lf + rg) >> 1;
        if(L <= mid)del(L, R, Ls);
        if(R > mid) del(L, R, Rs);
        nd->up();
    }
    return ;
}
int query(int L, int R, Node *nd, int lf = 1, int rg = n){
    if(nd->v == 0)return 0;
    if(L <= lf && rg <= R){
        int c = nd->v;
        nd->v = 0; 
        nd->tag = 0;
        return c;
    }
    int mid = (lf + rg) >> 1;
    nd->down(lf, rg);
    int ans = 0;
    if(L <= mid) ans += query(L, R, Ls);
    if(R > mid) ans += query(L, R, Rs);
    nd->up();
    return ans;
}


int main(){
//    
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    n = read(), m = read();
    int t = 0;
    char c = getchar();
    while(c<'a'||c>'z')c=getchar();
    while(c!='\n'){f[++t][c-'a'+1]=1;c=getchar();}
    For(i, 1, 26) root[i] = build(i);
    while(m--){
        int l = read(), r = read(), opt = read();
        For(i, 1, 26) cnt[i] = query(l, r, root[i]);
        int ed = l;
        if(opt) {
            For(i, 1, 26)
            if(cnt[i]) {
                del(ed, ed + cnt[i] - 1, root[i]);
                ed = ed + cnt[i];
            }
        }
        else {
            for(int i = 26; i; i--)
            if(cnt[i]) {
                del(ed, ed + cnt[i] - 1, root[i]);
                ed = ed + cnt[i];
            }
        }
    }
    For(i, 1, n)
        For(j, 1, 26)
            if(query(i, i, root[j])) putchar('a'+j-1);
    putchar('\n');
}
View Code

 

第二题:神奇的DP;

dp[i][j] 表示考虑到前i列,满足右区间的放了j个的方案数;sl, sr,是对左右区间记录的前缀和

这个转移=左方案*右方案;越过一个左侧区间的右端点时,从之前剩下的空列中选一列在这个左侧区间放1。转移时分在右侧区间放1或不放1;

考虑右边:可以不放dp[i-1][j], 也可以放f[i-1][j-1] * (sr[i] - (j-1));

考虑左边:对于每个在sl[i-1]到sl[i]的行枚举右边放了j个, 则有 (i - j - k) (k = sl[i-1] ~ sl[i] - 1) 位置可以选择;

#include<bits/stdc++.h>
using namespace std;
#define RG register
const int M = 3005;
#define ll long long
const ll mod=998244353;
ll f[M][M]; int sl[M], sr[M];

int main(){
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    int l, r, n, m;
    scanf("%d%d\n", &n, &m);
    for(int i=1;i<=n;i++){
        scanf("%d%d", &l, &r);
        sl[l]++, sr[r]++;
    }
    for(int i = 1; i <= m; i++) sl[i] += sl[i - 1], sr[i] += sr[i - 1];
    f[0][0] = 1;
    for(int i = 1; i <= m; i++){
        f[i][0] = f[i - 1][0];
        for(int j = 1; j <= i; j++)
            f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * (sr[i] - j + 1)) % mod;
        for(int k = sl[i-1]; k < sl[i]; k++)
            for(int j = 0; j < i; j++){
                f[i][j] = f[i][j] * (i - j - k) % mod;
            }
    }
    printf("%lld\n", f[m][n]);
    
    
    return 0;
}
View Code

 

 

第三题:Trie树;

对手做的是将x在二进制下左移一位。假设在异或i个数后左移,等价于开始时先左移,然后把前i个数左移一位。(这道题其他都想到了,就是这个没想到)
问题转化为选一个数,使它左移一位后,与m+1个给定数分别异或的最小值最大。将m+1个数建立一棵字典树,从上到下遍历来最大化结果:

走到一个点时,如果往下只有0,说明我们这一位取1异或后只能是1,累计结果中加上这一位的值,只有1也一样;如果既有0又有1,说明这一位无论怎么取最后都是0,分别往下走即可。

时间复杂度O(nm)。

 

#include<bits/stdc++.h>
using namespace std;

const int M = 1e5 + 5, ME = 5e6;
int a[M], pre[M], lst[M], bin[32], c[M];
int ch[ME][2];
int ans , cnt, tot, up, n;
void insert(int x){
    int now = 0;
    for(int i = n-1; i >= 0; i--){
        int t = x & bin[i] ? 1 : 0;
        if(!ch[now][t]) ch[now][t] = ++tot;
        now = ch[now][t];
    }
}
void dfs(int now, int dep, int val){
    if(dep==-1){
        if(val > ans) ans = val, cnt = 1;
        else if(val == ans) cnt ++;
        return ;
    }
    if(ch[now][0] && ch[now][1]){
        dfs(ch[now][0], dep-1, val);
        dfs(ch[now][1], dep-1, val);
    }
    else if(ch[now][1]){
        dfs(ch[now][1], dep-1, val+bin[dep]);
    } 
    else {
        dfs(ch[now][0], dep-1, val+bin[dep]);
    }
}

int main(){
    freopen("big.in","r",stdin);
    freopen("big.out","w",stdout);
    int m;
    scanf("%d%d", &n, &m);
    bin[0] = 1;
    for(int i = 1; i <= 30; i++) bin[i] = bin[i-1]<<1;
    for(int i = 1; i <= m; i++){
        scanf("%d", &a[i]);
        pre[i] = pre[i-1]^a[i];
    }
    up = 1<<n;
    for(int i = m; i; i--)lst[i] = lst[i+1]^a[i];
    for(int i = 0; i <= m; i++){
        int t = (long long)(pre[i]<<1)/up;
        c[i] = ((long long)(pre[i]<<1)&(up-1))+t;
        c[i] ^= lst[i+1];
        insert(c[i]);
    }
    dfs(0, n-1, 0);
    printf("%d\n%d\n", ans, cnt);
}
View Code

 

posted @ 2018-10-29 17:26  Ed_Sheeran  阅读(183)  评论(0编辑  收藏  举报