2015多校第三场总结
2015-10-30 15:11
253 查看
1001 Magician
线段树。维护区间四个值:奇奇,奇偶,偶奇,偶偶。更新父区间的每个值需注意:可由左儿子单独更新,可由右儿子单独更新,可由左右儿子结合一起更新,三种情况。
1005 Fan Li
gcd(i,j)的所有情况可由O(nlogn)求得。
对于最多区间数,用l[i]表示n==i时的最多区间数量,并且i是最后一个区间的右端点,用sum[i]表示l[i]对应的方案数。则,i由{j| l[i] = l[j]+1, j < i}更新。
考虑gcd=x对答案的贡献值
1006 Beautiful Set
计数问题,莫比乌斯反演。描述看似复杂,其实就是改写∑i∑fi
大致特点:
所有可能情况coni
对于coni,ans += f(coni)
考虑每个小部分对总体的贡献
如:
Mr. Hdu defines the beautiful value like this:
For k from 1 to n, choose k numbers of the set, and calculate the gcd of the k numbers. The beautiful value of the k numbers is k * (gcd of the k chosen numbers). The beautiful value of the set is the sum of all of the beautifu
l value of k numbers.
于是
ans = ∑ni=1∑s⊆[1,n],|s|=igcd(s)
考虑每个gcd对答案的贡献,得到
ans = ∑∞i=1∑s⊆[1,n]gcd(s)=i
对于gcd=i,统计有多少个集合的gcd为i。从而可以用莫比乌斯解决。
1005 Hope
NTT。Mod,N, g。N是2的幂,N | Mod-1,g是Mod原根。
单位根 wn = gMod−1N
读题要细致。理解题意,搞清数据范围。
递推式比较简单,
dp[i]=∑j=1iC(i−1,j−1)∗(j−1)!∗dp[i−j]∗j∗j
=∑j=1i(i−1)!∗dp[i−j]/(i−j)!∗j∗j
注意到这是一个卷积。但是每次要用到dp[0,i-1]来更新自己。
我们从递推的角度考虑,我们计算出dp[i]的时候,用dp[i]更新dp[j],j>i。
借鉴cdq分治的思想,用solve(l,r)表示用来求dp[l,r)。我们先求solve(l,(l+r)>>1),然后,我们计算dp[l,(l+r)>>1)对dp[l+r)>>1,r)的贡献。然后,solve((l+r)>>1,r)。
cdq分治,从左向右依次求得结果。先求左儿子,计算左儿子对右儿子的贡献,求右儿子。
不得不说,cdq这个方法太牛了!
1009 Boring Classes
经典的三维偏序,输出字典序最小的方案。
cdq分治。从右向左回溯答案得到一个DAG。字典序最小就是在这个DAG里找一条最长且字典序最小的路径。在这个DAG里对每个v求一个f[v],表示从v出发的最长路径。然后从左向右贪心求方案。
cdq分治的时候从右向左做可以省掉DAG过程。
1010 Crazy Bobo
博神A的题。想想当时自己还真是蠢,幸亏博神机智。就是找一棵子树,从该子树的根到该子树的任意节点都是递增序列。
线段树。维护区间四个值:奇奇,奇偶,偶奇,偶偶。更新父区间的每个值需注意:可由左儿子单独更新,可由右儿子单独更新,可由左右儿子结合一起更新,三种情况。
#define clr(A,x) memset(A,x,sizeof(A)) using namespace std; typedef long long LL; typedef pair<int,int> P; const int mod = 1000000007; const int mm = 100005; const int inf = 1000000007; int A[mm]; struct segment{ LL sum[mm<<2][4]; void pushup(LL a[],LL b[],LL c[]) { for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) a[i<<1|j] = max(b[i<<1|j],c[i<<1|j]); for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) for(int k = 0; k < 2; k++) a[i<<1|j] = max(a[i<<1|j],b[i<<1|k]+c[(k^1)<<1|j]); } void build(int rt,int l,int r) { if(l==r){ for(int i = 0; i < 4; i++) sum[rt][i] = -inf; if(l&1) sum[rt][3] = A[l]; else sum[rt][0] = A[l]; }else{ int mid = (l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); pushup(sum[rt],sum[rt<<1],sum[rt<<1|1]); } } void upd(int rt,int l,int r,int p,int x) { if(p < l || p > r) return ; if(l==p && p == r) { for(int i = 0; i < 4; i++) sum[rt][i] = -inf; if(l&1) sum[rt][3] = x; else sum[rt][0] = x; }else{ int mid = (l+r)>>1; upd(rt<<1,l,mid,p,x); upd(rt<<1|1,mid+1,r,p,x); pushup(sum[rt],sum[rt<<1],sum[rt<<1|1]); } } void query(int rt,int l,int r,int L,int R,LL *s){ if(r < L || R < l) return ; if(L <= l && r <= R){ for(int i = 0; i < 4; i++) s[i] = sum[rt][i]; return; } int mid = (l+r)>>1; if( R <= mid) { query(rt<<1,l,mid,L,R,s);return;} if( L > mid) { query(rt<<1|1,mid+1,r,L,R,s);return;} LL ls[4] = {-inf,-inf,-inf,-inf}; LL rs[4] = {-inf,-inf,-inf,-inf}; query(rt<<1,l,mid,L,R,ls); query(rt<<1|1,mid+1,r,L,R,rs); pushup(s,ls,rs); } }seg; int n,m; int main() { // freopen("wcbao.in","r",stdin); // freopen("1012.in","r",stdin); // freopen("out.txt","w",stdout); int T; cin >> T; while(T--) { cin >> n >> m; for(int i = 1; i <= n; i++) scanf("%d",A+i); int root = 1; seg.build(root,1,n); while(m--) { int op,x,y; scanf("%d%d%d",&op,&x,&y); if(op==0){ LL s[4] = {-inf,-inf,-inf,-inf}; seg.query(root,1,n,x,y,s); LL ans = -inf; for(int i = 0; i < 4; i++) ans = max(ans,s[i]); printf("%I64d\n",ans); }else{ seg.upd(root,1,n,x,y); } } } return 0; }
1005 Fan Li
gcd(i,j)的所有情况可由O(nlogn)求得。
对于最多区间数,用l[i]表示n==i时的最多区间数量,并且i是最后一个区间的右端点,用sum[i]表示l[i]对应的方案数。则,i由{j| l[i] = l[j]+1, j < i}更新。
考虑gcd=x对答案的贡献值
1006 Beautiful Set
计数问题,莫比乌斯反演。描述看似复杂,其实就是改写∑i∑fi
大致特点:
所有可能情况coni
对于coni,ans += f(coni)
考虑每个小部分对总体的贡献
如:
Mr. Hdu defines the beautiful value like this:
For k from 1 to n, choose k numbers of the set, and calculate the gcd of the k numbers. The beautiful value of the k numbers is k * (gcd of the k chosen numbers). The beautiful value of the set is the sum of all of the beautifu
l value of k numbers.
于是
ans = ∑ni=1∑s⊆[1,n],|s|=igcd(s)
考虑每个gcd对答案的贡献,得到
ans = ∑∞i=1∑s⊆[1,n]gcd(s)=i
对于gcd=i,统计有多少个集合的gcd为i。从而可以用莫比乌斯解决。
1005 Hope
NTT。Mod,N, g。N是2的幂,N | Mod-1,g是Mod原根。
单位根 wn = gMod−1N
读题要细致。理解题意,搞清数据范围。
递推式比较简单,
dp[i]=∑j=1iC(i−1,j−1)∗(j−1)!∗dp[i−j]∗j∗j
=∑j=1i(i−1)!∗dp[i−j]/(i−j)!∗j∗j
注意到这是一个卷积。但是每次要用到dp[0,i-1]来更新自己。
我们从递推的角度考虑,我们计算出dp[i]的时候,用dp[i]更新dp[j],j>i。
借鉴cdq分治的思想,用solve(l,r)表示用来求dp[l,r)。我们先求solve(l,(l+r)>>1),然后,我们计算dp[l,(l+r)>>1)对dp[l+r)>>1,r)的贡献。然后,solve((l+r)>>1,r)。
cdq分治,从左向右依次求得结果。先求左儿子,计算左儿子对右儿子的贡献,求右儿子。
不得不说,cdq这个方法太牛了!
#include <bits/stdc++.h> #define clr(A,x) memset(A,x,sizeof(A)) using namespace std; typedef long long LL; typedef pair<int,int> P; const int mm = 400005; const int mod = 998244353; const int Mod = 998244353; const int g = 3; LL fac[mm],inv[mm]; LL A[mm],B[mm]; LL a[mm],b[mm]; LL pw(LL x,int y){ LL res = 1; for(; y > 0; y >>= 1){ if(y&1) res = res *x % mod; x = x*x % mod; } return res; } void Rader(LL c[],int n) { int j = n >> 1; for(int i = 1; i < n - 1; i++) { if(i < j) swap(c[i],c[j]); int k = n >> 1; for(; j >= k; k >>= 1) j -= k; j |= k; } } void NTT(LL c[],int n,int I) { Rader(c,n); for(int L = 2; L <= n; L <<= 1) { LL wn = pw(g,(mod-1)/L); if(I == -1) wn = pw(wn,mod-2); for(int j = 0; j < n; j += L){ LL w = 1; for(int k = j; k < j + L/2; k++) { LL u = c[k], v = c[k+L/2]; c[k] = (u + w*v % mod) % mod; c[k+L/2] = (u - w*v % mod) % mod; w = w * wn % mod; } } } if(I == -1){ int INV = pw(n,mod-2); for(int i = 0; i < n; i++) c[i] = c[i]*INV % mod; } } void solve(int l,int r) { if(l == r) return ; int mid = (l+r)>>1; solve(l,mid); int len = r-l+1, n = 1; while(n < len+len) n <<= 1; for(int i = 0; i <= n+2; i++) a[i] = b[i] = 0; for(int i = 1; i <= r-l; i++) a[i] = 1ll*i*i % mod; for(int i = l; i <= mid; i++) b[i-l] = B[i]; NTT(a,n,1);NTT(b,n,1); for(int i = 0; i < n; i++) b[i] = a[i] * b[i] % mod; NTT(b,n,-1); for(int i = mid+1; i <= r; i++) B[i] = (B[i] + b[i-l]*inv[i] % mod) % mod; solve(mid+1,r); } void init(int n) { fac[0] = inv[0] = 1; for(int i = 1; i <= n; i++){ fac[i] = fac[i-1]*i % mod; inv[i] = pw(i,mod-2); } clr(B,0); B[0] = 1; solve(0,n); } int main() { // freopen("wcbao.in","r",stdin); // freopen("wcbao.out","w",stdout); int n = 100000; init(n); for(int i = 0; i <= n; i++) B[i] = (B[i]*fac[i] % mod + mod ) % mod; while(cin >> n) { cout << B << endl; } return 0; }
1009 Boring Classes
经典的三维偏序,输出字典序最小的方案。
cdq分治。从右向左回溯答案得到一个DAG。字典序最小就是在这个DAG里找一条最长且字典序最小的路径。在这个DAG里对每个v求一个f[v],表示从v出发的最长路径。然后从左向右贪心求方案。
cdq分治的时候从右向左做可以省掉DAG过程。
#define PI acos(-1) #define clr(A,x) memset(A,x,sizeof(A)) using namespace std; typedef long long LL; typedef pair<int,int> P; typedef pair<int,P> pp; typedef complex<double> cpx; const int mm = 100005; const int mod = 1000000007; const int inf = 1000000007; int n; int L[mm],R[mm],len[mm]; int p[mm],prv[mm]; int cmp(int i,int j){ return L[i] < L[j] || (L[i] == L[j] && i > j); } vector<int> dis; void discrete(int a[],int b[],int n) { dis.clear(); for(int i = 0; i < n; i++) dis.push_back(a[i]); sort(dis.begin(),dis.end()); dis.erase(unique(dis.begin(),dis.end()),dis.end()); for(int i = 0; i < n; i++) b[i] = lower_bound(dis.begin(),dis.end(),a[i]) - dis.begin() + 1; } int C[mm]; int Max(int x,int y){ if(len[x] != len[y]) return len[x] > len[y] ? x : y; return x < y ? x : y; } void modify(int c[],int x,int id,int N){ int dx = len[id]; for( ; x < N; x += x&-x) // c[x] = max(c[x],dx); c[x] = Max(c[x],id); } int getMax(int c[],int x){ int res = c[x]; for( ; x > 0; x -= x&-x) // res = max(res,c[x]); res = Max(res,c[x]); return res; } int b[mm]; void solve(int l,int r){ if(l == r){ len[l] = max(len[l],1); return ; } int mid = (l+r)>>1; solve(mid+1,r); for(int i = l; i <= r; i++) p[i] = i; sort(p+l,p+r+1,cmp); for(int i = 0; i <= r-l+1; i++) C[i] = 0; discrete(R+l,b+l,r-l+1); int N = r - l + 3; for(int i = l; i <= r; i++){ int &id = p[i]; if(id > mid){ modify(C,b[id],id,N); }else{ int x = getMax(C,b[id]); // len[id] = max(x+1,len[id]); if(len[id] <= len[x]+1){ len[id] = len[x] + 1; prv[id] = x; } } } solve(l,mid); } int main() { // freopen("wcbao.in","r",stdin); while(cin >> n) { for(int i = 1; i <= n; i++) { scanf("%d",L+i); } for(int i = 1; i <= n; i++) { scanf("%d",R+i); R[i] = inf - R[i]; } discrete(R+1,R+1,n); clr(len,0);clr(prv,0); solve(1,n); int s = 1; for(int i = 1; i <= n; i++) if(len[i] > len[s]) s = i; int ans = len[s]; cout << ans << endl; // for(int i = 1; i <= n; i++) // printf("%d%c",len[i],i==n?'\n':' '); for(int i = 0; i < ans; i++) { printf("%d%c",s,i==ans-1?'\n':' '); s = prv[s]; } } return 0; }
1010 Crazy Bobo
博神A的题。想想当时自己还真是蠢,幸亏博神机智。就是找一棵子树,从该子树的根到该子树的任意节点都是递增序列。
相关文章推荐
- 简单的四则运算
- 数的奇偶性
- 网络资料备忘录
- 谱相关的资料备忘
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 矩阵的乘法操作
- 蚂蚁爬行问题
- 蚂蚁爬行问题
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置
- 杭电题目---一只小蜜蜂
- HDOJ 1002 A + B Problem II (Big Numbers Addition)
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1002
- 一幅图弄清DFT与DTFT,DFS的关系
- POJ 2635 The Embarrassed Cryptographe