NOIP模拟赛17
5分。。。。
T1 LOJ 计算几何瞎暴力
维护以下操作:
1、序列末尾加一个数
2、序列全体从小到大排序
3、查询区间和
4、序列全体异或一个数k
序列全体异或一个数,很明显是trie树
那么序列全体从大到小排序就是把一个个数插入trie树的过程
那么就需要一个数组,存储还没有插入trie树的数
全体异或:
这里需要两个标记:
xortag表示当前序列是异或了什么,
trie树里1个tag 表示trie树是在异或了tag之后有序
为什么需要标记?
假设当前是二进制第i位,如果i&标记的第i位为真,那么查询的时候1相当于0,0相当于1
为什么要两个标记?
因为有可能对全体执行异或操作,这样原来有序的trie树直接异或就无序了。
所以tag用来表示trie树在当前tag下是有序的序列
查询子树和时,用xortag
在判断向左还是向右走时,要用trie树里的tag
因为trie树里的顺序不变
有了标记,查询区间和时,需要维护的就是二进制的每一位1的出现次数
如果&标记的第i为为真,就用0的个数
否则用1的个数
#include<cstdio> #include<iostream> using namespace std; typedef long long LL; #define N 200001 void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int xortag,bit[31]; struct TRIE { int len,tag,root; int ch[N*31][2],sum[N*31][31],siz[N*31]; TRIE() { len=0; tag=0; root=0; } void insert(int x) { int rt=root,id; for(int i=29;i>=0;--i) { id=(x&bit[i])>0; if(!ch[rt][id]) ch[rt][id]=++len; rt=ch[rt][id]; siz[rt]++; for(int j=0;j<30;++j) if(x&bit[j]) sum[rt][j]++; } } LL getsum(int x) { LL ans=0; for(int i=0;i<30;++i) if(xortag&bit[i]) ans+=1ll*(siz[x]-sum[x][i])*bit[i]; else ans+=1ll*sum[x][i]*bit[i]; return ans; } LL query(int x) { if(!x) return 0; int rt=root,l,r; LL ans=0; for(int i=29;i>=0;--i) { l=0,r=1; if(tag&bit[i]) swap(l,r); if(x<=siz[ch[rt][l]]) rt=ch[rt][l]; else { ans+=getsum(ch[rt][l]); x-=siz[ch[rt][l]]; rt=ch[rt][r]; } } ans+=getsum(rt)/siz[rt]*x; return ans; } int getsiz() { return siz[ch[root][0]]+siz[ch[root][1]]; } }Trie; struct ARRAY { int len; ARRAY() { len=0; } int sum[N][31],a[N]; void insert(int x) { x^=xortag; a[++len]=x; for(int i=29;i>=0;i--) if(x&bit[i]) sum[len][i]=sum[len-1][i]+1; else sum[len][i]=sum[len-1][i]; } void transfer() { Trie.tag=xortag; for(int i=1;i<=len;i++) Trie.insert(a[i]); len=0; } LL query(int x) { LL ans=0; for(int i=0;i<30;++i) if(xortag&bit[i]) ans+=1ll*(x-sum[x][i])*bit[i]; else ans+=1ll*sum[x][i]*bit[i]; return ans; } }Array; LL query(int x) { int siz=Trie.getsiz(); if(x<=siz) return Trie.query(x); else return Trie.query(siz)+Array.query(x-siz); } int main() { freopen("seko.in","r",stdin); freopen("seko.out","w",stdout); bit[0]=1; for(int i=1;i<=30;i++) bit[i]=bit[i-1]<<1; int n,m,x; read(n); for(int i=1;i<=n;++i) read(x),Array.insert(x); read(m); int ty,u,v; while(m--) { read(ty); if(ty==1) { read(v); Array.insert(v); } else if(ty==2) Array.transfer(); else if(ty==3) { read(u);read(v); printf("%I64d\n",query(v)-query(u-1)); } else { read(v); xortag^=v; } } return 0; }
T2 [ZJOI2008]骑士
基环树DP,断环为链合并
为什么做最大点权独立集不对?
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 1000001 using namespace std; typedef long long LL; int n,num; int to[N<<1],nxt[N<<1],front[N],val[N]; int dfn[N],low[N],st[N],top,tot=1,ai[N]; bool cir[N],vis[N]; LL dp[N][2],bi[N][2],ans2,ans; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void add(int u,int v) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; } void init() { read(n); int y; for(int i=1;i<=n;i++) { read(val[i]),read(y); add(i,y); } } void tarjan(int u,int pre) { low[u]=dfn[u]=++tot; st[++top]=u; vis[u]=true; for(int i=front[u];i;i=nxt[i]) { if(i==(pre^1)) continue; if(!dfn[to[i]]) { tarjan(to[i],i); low[u]=min(low[u],low[to[i]]); } else low[u]=min(low[u],dfn[to[i]]),vis[to[i]]=true; } if(low[u]==dfn[u]) { if(st[top]==u) top--; else { while(st[top]!=u) { cir[st[top]]=true; ai[++num]=st[top--]; } top--,ai[++num]=u; cir[u]=true; } } } void dfs(int x,int fa) { dp[x][1]=val[x]; int t; for(int i=front[x];i;i=nxt[i]) { t=to[i]; if(cir[t] || t==fa) continue; dfs(t,x); dp[x][0]+=max(dp[t][0],dp[t][1]); dp[x][1]+=dp[t][0]; } } void unionn() { int tot,t; LL tmp; for(int i=1;i<=n;i++) { if(vis[i]) continue; ans=0;num=0;top=0; tarjan(i,0); for(int i=1;i<=num;i++) dfs(ai[i],0); bi[1][0]=dp[ai[1]][0],bi[1][1]=0; for(int i=2;i<=num;i++) { bi[i][0]=dp[ai[i]][0]; bi[i][1]=dp[ai[i]][1]; bi[i][0]+=max(bi[i-1][0],bi[i-1][1]); bi[i][1]+=bi[i-1][0]; } ans=max(bi[num][1],bi[num][0]); bi[1][0]=0,bi[1][1]=dp[ai[1]][1]; for(int i=2;i<=num;i++) { bi[i][0]=dp[ai[i]][0]; bi[i][1]=dp[ai[i]][1]; bi[i][0]+=max(bi[i-1][0],bi[i-1][1]); bi[i][1]+=bi[i-1][0]; } ans=max(ans,bi[num][0]); ans2+=ans; } printf("%lld",ans2); } int main() { //freopen("hoyin.in","r",stdin); // freopen("hoyin.out","w",stdout); init(); unionn(); }
T3 [Ynoi2017]由乃的玉米田
http://www.lydsy.com/JudgeOnline/problem.php?id=4810
莫队+bitset
维护两个bitset
第i位表示是否存在值为i的数
一个正着的A,一个反着的B
1、xi-xj=d ==> xi=d+xj,所以如果 A&A(<<d)有一位为真,就存在 (右移也行)
2、xi+xj=d ==> xi=-xj+d,有负数不好处理,所以 ==> xi=N-xj+d-N=(N-xj)-(N-d)
所以若A&(B>>(N-d))有一位为真,就存在
3、xi*xj=d,根号d枚举d的约数,判断是否存在
#include<cstdio> #include<bitset> #include<iostream> #include<algorithm> #include<cmath> #define N 100001 using namespace std; bitset <N> A,B; int n,m,siz; int a[N],bl[N]; int sum[N]; bool ans[N]; struct node { int ty,l,r,d; int id; }e[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } bool cmp(node p,node q) { if(bl[p.l]!=bl[q.l]) return bl[p.l]<bl[q.l]; return p.r<q.r; } void update(int pos,bool w) { if(w) { A[a[pos]]=1; B[N-a[pos]]=1; sum[a[pos]]++; } else { sum[a[pos]]--; if(!sum[a[pos]]) A[a[pos]]=0,B[N-a[pos]]=0; } } bool solve(int i) { if(e[i].ty==1) return (A&(A<<e[i].d)).any(); if(e[i].ty==2) return (A&(B>>(N-e[i].d))).any(); int d=e[i].d; if(!d) return sum[0]; for(int j=1;j*j<=d;j++) if(!(d%j) && sum[j] && sum[d/j]) return true; return false; } int main() { freopen("baka.in","r",stdin); freopen("baka.out","w",stdout); read(n); read(m); siz=sqrt(n); for(int i=1;i<=n;i++) bl[i]=(i-1)/siz+1; for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=m;i++) { read(e[i].ty);read(e[i].l);read(e[i].r);read(e[i].d); e[i].id=i; } sort(e+1,e+m+1,cmp); int L=1,R=0,l,r; for(int i=1;i<=m;i++) { l=e[i].l; r=e[i].r; while(R<r) update(++R,1); while(R>r) update(R--,0); while(L<l) update(L++,0); while(L>l) update(--L,1); ans[e[i].id]=solve(i); } for(int i=1;i<=m;i++) puts(ans[i] ? "seko" : "baka"); }