今天没有一道题想到正解,全部打的暴力,今天题思维很活啊;
第一题:主要是利用只有26个字母,往这方面想了,但失败了;
计数排序:主要针对数据很集中的;
建26棵线段树,记录区间中该数有多少个,每次查询区间中26个字母各有多少个,按顺序排列;就一个区间查询,修改操作,此题卡常;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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!=' '){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(' '); }
第二题:神奇的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) 位置可以选择;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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, &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 ", f[m][n]); return 0; }
第三题:Trie树;
对手做的是将x在二进制下左移一位。假设在异或i个数后左移,等价于开始时先左移,然后把前i个数左移一位。(这道题其他都想到了,就是这个没想到)
问题转化为选一个数,使它左移一位后,与m+1个给定数分别异或的最小值最大。将m+1个数建立一棵字典树,从上到下遍历来最大化结果:
走到一个点时,如果往下只有0,说明我们这一位取1异或后只能是1,累计结果中加上这一位的值,只有1也一样;如果既有0又有1,说明这一位无论怎么取最后都是0,分别往下走即可。
时间复杂度O(nm)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 %d ", ans, cnt); }