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 }
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 }
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 }
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 }
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 }