NOIP2018训练题集

1. CZday3C

给定有m个数的集合,从其中任选一个子集满足全部&后不为零,问方案数。

考虑对二进制位容斥,问题转化为求包含某个二进制位集合的数的个数,通过类似FMT的DP求解。

 1 #include<bits/stdc++.h>
 2 #define mo 1000000007
 3 #define ll long long
 4 using namespace std;
 5 ll m,n,k,f[1100010],g[1100010],x,ans;
 6 ll po(ll x,ll y){ll z=1;while (y){if (y%2==1)z=(x*z)%mo;x=(x*x)%mo;y/=2;}return z;}
 7 int main(){
 8     freopen("loneliness.in","r",stdin);
 9     freopen("loneliness.out","w",stdout);
10     cin>>n;
11     cin>>n>>m>>k;
12     for (int i=1;i<=m;i++){scanf("%d",&x);g[x]++;}
13     f[0]=-1;
14     for (int i=0;i<(1<<n);i++)
15         for (int j=1;j<(1<<n);j*=2)
16             if ((i&j)==0)f[i+j]=f[i]*(-1);
17     for (int i=1;i<(1<<n);i*=2)
18         for (int j=0;j<(1<<n);j++)
19             if ((i&j)!=0) g[j-i]+=g[j];
20     for (int i=1;i<(1<<n);i++)ans=(ans+f[i]*po(g[i],k)+mo)%mo;
21     cout<<ans<<endl;
22     return 0;
23 }
View Code

2.CZday6C

在线修改,查询第一个比前面所有数之和大的数。

类似FRBSUM,如果一个数不是答案,则下一个答案必然不小于从头一直加到这个数的和,这样倍增查询每次至少翻倍,线段树支持即可。

 1 #include<bits/stdc++.h>
 2 #define maxn 200010
 3 #define N 800010
 4 #define ll long long
 5 using namespace std;
 6 int value[maxn], mx[N];
 7 ll sum[N];
 8 void build(int now, int l, int r)
 9 {
10     if (l == r)
11     {
12         mx[now] = sum[now] = value[l];
13         return;
14     }
15     int mid = (l + r) / 2;
16     build(now * 2, l, mid);
17     build(now * 2 + 1, mid + 1, r);
18     mx[now] = max(mx[now * 2], mx[now * 2 + 1]);
19     sum[now] = sum[now * 2] + sum[now * 2 + 1];
20 }
21 void modify(int now, int l, int r, int x, int y)
22 {
23     if (l == r)
24     {
25         mx[now] = sum[now] = value[l];
26         return;
27     }
28     int mid = (l + r) / 2;
29     if (x <= mid) modify(now * 2, l, mid, x, y);
30     else modify(now * 2 + 1, mid + 1, r, x, y);
31     mx[now] = max(mx[now * 2], mx[now * 2 + 1]);
32     sum[now] = sum[now * 2] + sum[now * 2 + 1];
33 }
34 ll query_sum(int now, int l, int r, int left, int right)
35 {
36     if (l == left && r == right) return sum[now];
37     int mid = (l + r) / 2; ll ans = 0;
38     if (left <= mid) ans = query_sum(now * 2, l, mid, left, min(right, mid));
39     if (right >= mid + 1) ans += query_sum(now * 2 + 1, mid + 1, r, max(left, mid + 1), right);
40     return ans;
41 }
42 int query(int now, int l, int r, int left, int right, ll x)
43 {
44     int mid = (l + r) / 2;
45     if (l == left && r == right)
46     {
47         if (mx[now] < x) return -1;
48         if (l == r) return l;
49         if (mx[now * 2] >= x) return query(now * 2, l, mid, l, mid, x);
50         else return query(now * 2 + 1, mid + 1, r, mid + 1, r, x);
51     }
52     int ans = -1;
53     if (left <= mid) ans = query(now * 2, l, mid, left, min(right, mid), x);
54     if (ans != -1) return ans;
55     else return query(now * 2 + 1, mid + 1, r, max(left, mid + 1), right, x);
56 }
57 int main()
58 {
59     freopen("challenge.in", "r", stdin);
60     freopen("challenge.out", "w", stdout);
61     int n, q;
62     scanf("%d%d", &n, &q);
63     for (int i = 1; i <= n; i++)
64         scanf("%d", &value[i]);
65     build(1, 1, n);
66     for (int i = 1; i <= q; i++)
67     {
68         int x, y;
69         scanf("%d%d", &x, &y);
70         value[x] = y;
71         modify(1, 1, n, x, y);
72         int cur = 0, res = -1; ll pre = 0;
73         while (cur < n)
74         {
75             int tmp = query(1, 1, n, cur + 1, n, pre);
76             if (tmp == -1) break;
77             cur = tmp; pre = query_sum(1, 1, n, 1, tmp);
78             if (pre - value[cur] == value[cur]) {res = tmp; break;}
79         }
80         printf("%d\n", res);
81     }
82     return 0;
83 }
View Code

3.ACday3C

n个数,m个等级(m<=10),某个数不小于某值ai就是i级。在线区间加,单点修改,询问区间所有数等级和。

线段树,Min[x]表示这个区间中最小的离下一级的距离。更新时如果Min[x]>0则退出,否则递归下去。均摊复杂度nmlogn。类似区间开方之类的线段树均摊操作。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define ls (x<<1)
 4 #define rs (ls|1)
 5 #define lson ls,L,mid
 6 #define rson rs,mid+1,R
 7 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 8 using namespace std;
 9 
10 const int N=500010;
11 int n,m,Q,op,x,y,k,a[12],c[N],sm[N],mn[N],tag[N];
12 
13 int chk(int v){ for (int i=m; ~i; i--) if (v>=a[i]) return i; return 0; }
14 void upd(int x){ sm[x]=sm[ls]+sm[rs]; mn[x]=min(mn[ls],mn[rs]); }
15 
16 void push(int x){
17     if (!tag[x]) return;
18     mn[ls]+=tag[x]; tag[ls]+=tag[x];
19     mn[rs]+=tag[x]; tag[rs]+=tag[x];
20     tag[x]=0;
21 }
22 
23 void build(int x,int L,int R){
24     if (L==R){ sm[x]=chk(c[L]); mn[x]=a[sm[x]+1]-c[L]; return; }
25     int mid=(L+R)>>1;
26     build(lson); build(rson); upd(x);
27 }
28 
29 void work(int x,int L,int R){
30     if (mn[x]>0) return;
31     if (L==R){
32         while (mn[x]<=0) sm[x]++,mn[x]=a[sm[x]+1]-a[sm[x]]+mn[x];
33         return;
34     }
35     push(x); int mid=(L+R)>>1;
36     work(lson); work(rson); upd(x);
37 }
38 
39 void add(int x,int L,int R,int l,int r,int k){
40     if (L==l && r==R){ mn[x]-=k; tag[x]-=k; work(x,L,R); return; }
41     push(x); int mid=(L+R)>>1;
42     if (r<=mid) add(lson,l,r,k);
43     else if (l>mid) add(rson,l,r,k);
44         else add(lson,l,mid,k),add(rson,mid+1,r,k);
45     upd(x);
46 }
47 
48 void mdf(int x,int L,int R,int pos,int k){
49     if (L==R){ sm[x]=chk(k); mn[x]=a[sm[x]+1]-k; return; }
50     push(x); int mid=(L+R)>>1;
51     if (pos<=mid) mdf(lson,pos,k); else mdf(rson,pos,k);
52     upd(x);
53 }
54 
55 int que(int x,int L,int R,int l,int r){
56     if (L==l && r==R) return sm[x];
57     push(x); int mid=(L+R)>>1;
58     if (r<=mid) return que(lson,l,r);
59     else if (l>mid) return que(rson,l,r);
60         else return que(lson,l,mid)+que(rson,mid+1,r);
61 }
62 
63 int main(){
64     scanf("%d%d%d",&n,&m,&Q);
65     rep(i,1,m) scanf("%d",&a[i]);
66     a[0]=-2e9; a[m+1]=2e9;
67     rep(i,1,n) scanf("%d",&c[i]);
68     build(1,1,n);
69     while (Q--){
70         scanf("%d%d%d",&op,&x,&y);
71         if (op==1) scanf("%d",&k),add(1,1,n,x,y,k);
72         else if (op==2) mdf(1,1,n,x,y);
73             else printf("%d\n",que(1,1,n,x,y));
74     }
75     return 0;
76 }
View Code

4.ACday4C

一棵树和一些边,在线询问只取[L,R]中的边时,S到T的边权异或和最小的路径。

非常好的一道题,发现一条边的贡献一定是和树边组成的环的异或和,线性基加优化求解。

http://noi.ac/contest/13/problem/41

https://www.cnblogs.com/Gloid/p/9658421.html

5.NOWday1C

一棵树一些路径,每次询问一个点vi的最远祖先u,满足uv之间的路径被至少ki条路径完全包含。

可以DFS化成二维数点问题主席树和倍增解决,也可以每个点记录子树内最远的一个是某个路径拐点(LCA)的祖先,线段树合并即可。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define lson ls[x],L,mid
 6 #define rson rs[x],mid+1,R
 7 #define rep(i,l,r) for (int i=l; i<=r; i++)
 8 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 9 typedef long long ll;
10 using namespace std;
11  
12 const int N=200010;
13 int n,m,u,v,k,l,cnt,nd,Q,rt[N],fa[N][19],dep[N];
14 int h[N],nxt[N<<1],to[N<<1],sz[N*80],ls[N*80],rs[N*80];
15 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
16  
17 void dfs(int x){
18     dep[x]=dep[fa[x][0]]+1;
19     rep(i,1,18) fa[x][i]=fa[fa[x][i-1]][i-1];
20     For(i,x) if ((k=to[i])!=fa[x][0]) fa[k][0]=x,dfs(k);
21 }
22  
23 int lca(int u,int v){
24     if (dep[u]<dep[v]) swap(u,v);
25     int t=dep[u]-dep[v];
26     for (int i=18; ~i; i--) if (t&(1<<i)) u=fa[u][i];
27     if (u==v) return u;
28     for (int i=18; ~i; i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
29     return fa[u][0];
30 }
31  
32 void ins(int &x,int L,int R,int k){
33     if (!x) x=++nd; sz[x]++;
34     if (L==R) return;
35     int mid=(L+R)>>1;
36     if (k<=mid) ins(lson,k); else ins(rson,k);
37 }
38  
39 int merge(int x,int y){
40     if (!x || !y) return x+y;
41     int p=++nd; sz[p]=sz[x]+sz[y];
42     ls[p]=merge(ls[x],ls[y]); rs[p]=merge(rs[x],rs[y]);
43     return p;
44 }
45  
46 void dfs2(int x){
47     For(i,x) if ((k=to[i])!=fa[x][0]) dfs2(k),rt[x]=merge(rt[x],rt[k]);
48 }
49  
50 int que(int x,int L,int R,int k){
51     if (L==R) return L;
52     int mid=(L+R)>>1;
53     if (k<=sz[ls[x]]) return que(lson,k); else return que(rson,k-sz[ls[x]]);
54 }
55  
56 int main(){
57     scanf("%d%d",&n,&m);
58     rep(i,2,n) scanf("%d%d",&u,&v),add(u,v),add(v,u);
59     dfs(1);
60     rep(i,1,m) scanf("%d%d",&u,&v),l=lca(u,v),ins(rt[v],1,n,dep[l]),ins(rt[u],1,n,dep[l]);
61     dfs2(1);
62     for (scanf("%d",&Q); Q--; ) scanf("%d%d",&v,&k),printf("%d\n",max(dep[v]-que(rt[v],1,n,k),0));
63     return 0;
64 }
View Code

6.NOWday2B

给换上每个位置i一个[1,ai]中的整数,任意两个相邻位置数不相同,求方案数。

考虑容斥,到位置i时,枚举i与i-1,...,i-k+1都相等,转移为$f_i=\sum\limits_{j} f_j\times min(a_{j+1..i})\times (-1)^{i-j-1}$

单调队列优化,这里也比较巧妙,具体看代码。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5 using namespace std;
 6  
 7 const int N=2e6+7,mod=1e9+7;
 8 ll n,a[N],stk[N][3],f[N],ans,tmp,mn;
 9  
10 int main(){
11     scanf("%lld",&n); a[0]=1e9+8;
12     rep(i,1,n){
13         scanf("%lld",&a[i]);a[i+n]=a[i];
14         if(a[i]<a[mn])mn=i;
15     }
16     rep(i,1,n) a[i]=a[i+mn-1];
17     f[0]=n&1?-1:1; int t=1;
18     stk[1][0]=1e9+8; stk[1][1]=f[0];
19     rep(i,1,n){
20         int sum=0;
21         while(t&&a[i]<stk[t][0])sum=(sum+stk[t--][1])%mod;
22         f[i]=((f[i]-stk[t][2]-1ll*sum*a[i]%mod)%mod+mod)%mod;
23         stk[++t][0]=a[i];
24         stk[t][1]=sum;
25         stk[t][2]=(1ll*sum*a[i]+stk[t-1][2])%mod;
26         stk[++t][0]=1e9+8;
27         stk[t][1]=f[i];
28         ans=(ans+f[i])%mod;
29     }
30     ans=(ans+(n&1?-a[1]:a[1]))%mod;
31     printf("%lld",ans);
32     return 0;
33 }
View Code

猜你喜欢

转载自www.cnblogs.com/HocRiser/p/9672336.html
今日推荐