您的位置:首页 > 其它

2015多校第三场总结

2015-10-30 15:11 169 查看
1001 Magician

线段树。维护区间四个值:奇奇,奇偶,偶奇,偶偶。更新父区间的每个值需注意:可由左儿子单独更新,可由右儿子单独更新,可由左右儿子结合一起更新,三种情况。

#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 FFT cdq sigma