[bzoj4722] 由乃

Posted HLX_Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj4722] 由乃相关的知识,希望对你有一定的参考价值。

 

题意:两种操作。1、将所给区间划分成两个集合,集合中的元素的贡献为a[i]+1,求是否能找出两个集合使两个集合的总贡献相等。2、区间立方(有模数)

 

题解:

倍增+线段树+搜索(meet in the middle)

区间立方:由于模数比较小,我们可以用倍增预处理某个数的2^j次立方,然后用线段树维护查询的次数,单次复杂度:O(logn)

划分集合:

首先有个结论,由于所有数的最大范围只有1000,所以超过13个数一定能通过加减等于0

对于范围小于等于13的区间,首先查出每个数操作后得到的数,然后用meet in the middle暴搜出解,单次复杂度:O(logn+3^7)

总复杂大概是:O(m*(logn+3^7))

然后这题我犯了一大堆傻逼错误,调了一个下午,我也是醉了

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<cmath>
  7 #define ll long long
  8 #define ls x<<1
  9 #define rs x<<1|1
 10 #define N 100010
 11 #define M 15000
 12 using namespace std;
 13 
 14 int n,m,v,top;
 15 int a[N],tr[N*4],lazy[N*4],f[1010][22],st[N],val[20];
 16 bool flg,vis[N];
 17 
 18 int gi() {
 19   int x=0,o=1; char ch=getchar();
 20   while(ch!=- && (ch<0 || ch>9)) ch=getchar();
 21   if(ch==-) o=-1,ch=getchar();
 22   while(ch>=0 && ch<=9) x=x*10+ch-0,ch=getchar();
 23   return o*x;
 24 }
 25 
 26 void pre() {
 27   for(int i=0; i<v; i++) f[i][0]=i*i*i%v;//错点1:初始化对象错误
 28   for(int j=1; j<=20; j++)
 29     for(int i=0; i<v; i++)
 30       f[i][j]=f[f[i][j-1]][j-1];
 31 }
 32 
 33 void pushdown(int x) {
 34   if(!lazy[x]) return;
 35   tr[ls]+=lazy[x],tr[rs]+=lazy[x];
 36   lazy[ls]+=lazy[x],lazy[rs]+=lazy[x];
 37   lazy[x]=0;
 38 }
 39 
 40 void update(int x, int l, int r, int ql, int qr) {
 41   if(ql<=l && r<=qr) {
 42     tr[x]++,lazy[x]++;//其实只要开一个就够了,反正每次只会查询叶子= =、
 43     return;
 44   }
 45   pushdown(x);
 46   int mid=(l+r)>>1;
 47   if(qr<=mid) update(ls,l,mid,ql,qr);
 48   else if(ql>mid) update(rs,mid+1,r,ql,qr);
 49   else update(ls,l,mid,ql,mid),update(rs,mid+1,r,mid+1,qr);
 50 }
 51 
 52 int query(int x, int l, int r, int qx) {
 53   if(l==r) return tr[x];
 54   pushdown(x);//错点3:查询时没有pushdown
 55   int mid=(l+r)>>1;
 56   if(qx<=mid) return query(ls,l,mid,qx);
 57   else return query(rs,mid+1,r,qx);
 58 }
 59 
 60 int get(int x) {
 61   int y=query(1,1,n,x),ret=a[x];
 62   for(int i=20; i>=0; i--) {
 63     if(y&(1<<i)) ret=f[ret][i];
 64   }
 65   return ret;
 66 }
 67 
 68 void dfs1(int dep, int k, int sum, int siz) {
 69   if(dep>k) {
 70     vis[sum+M]=1,st[++top]=sum;
 71     if(!sum && siz) flg=1;
 72     return;
 73   }
 74   dfs1(dep+1,k,sum,siz);
 75   if(flg) return;
 76   dfs1(dep+1,k,sum+val[dep]+1,siz+1);
 77   if(flg) return;
 78   dfs1(dep+1,k,sum-val[dep]-1,siz+1);
 79 }
 80 
 81 void dfs2(int dep, int k, int sum, int siz) {
 82   if(dep>k) {
 83     if((vis[-sum+M] && sum!=0) || (!sum && siz)) flg=1;
 84     return;
 85   }
 86   dfs2(dep+1,k,sum,siz);
 87   if(flg) return;//错点5:没打分号
 88   dfs2(dep+1,k,sum+val[dep]+1,siz+1);
 89   if(flg) return;
 90   dfs2(dep+1,k,sum-val[dep]-1,siz+1);
 91 }
 92 
 93 bool check(int x) {
 94   for(int i=1; i<=top; i++) vis[st[i]+M]=0;
 95   top=0,flg=0;
 96   dfs1(1,x/2,0,0);
 97   if(flg) return true;
 98   dfs2(x/2+1,x,0,0);
 99   if(flg) return true;
100   return false;//错点4:返回false时没有清空数组
101 }
102 
103 int main() {
104   n=gi(),m=gi(),v=gi();
105   for(int i=1; i<=n; i++) a[i]=gi();
106   pre();
107   for(int i=1; i<=m; i++) {
108     int op=gi(),l=gi(),r=gi();
109     if(op==2) update(1,1,n,l,r); 
110     else {
111       if(r-l+1>13) puts("Yuno");
112       else {
113       for(int j=l; j<=r; j++) val[j-l+1]=get(j);//错点2:重复用i循环
114       if(check(r-l+1)) puts("Yuno");
115       else puts("Yuki");
116       }
117     }
118   }
119   return 0;
120 }

 

以上是关于[bzoj4722] 由乃的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4811[Ynoi2017]由乃的OJ 树链剖分+线段树

BZOJ4810: [Ynoi2017]由乃的玉米田

bzoj4811[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

莫队bzoj4866: [Ynoi2017]由乃的商场之旅

[BZOJ]4810: [Ynoi2017]由乃的玉米田

bzoj4810 [Ynoi2017]由乃的玉米田