2021.7.29考试总结[NOIP模拟27]
T1 牛半仙的妹子图
做法挺多的,可以最小生成树或者最短路,复杂度O(cq),c是颜色数。
我考场上想到了原来做过的一道题影子,就用了并查集,把边权排序后一个个插入,记录权值的前缀和,复杂度mlogm挺优秀。
后来发现wlr都是1e9,一个个求前缀和直接炸了,考场上感觉l,r,w差值对答案有影响就没离散化,开了个map记出现的w的前缀和,其他都能O1计算。
这不切了吗?年轻的我如是想到。
于是我领略到了map80倍常数的威力。离散化开数组再带到初值计算就A了。
考场拿了75pts还WA了仨点,据WTZ说要大力分类讨论,但我改对离散化直接切了,不大懂。
code:
1 #include<bits/stdc++.h> 2 #define debug exit(0) 3 #define int long long 4 #define rin register signed 5 using namespace std; 6 const int NN=5e5+5; 7 struct edge{ 8 int st,to,nex,w; 9 }e[NN]; 10 struct bcj{ 11 int fa; 12 bitset<650>gl; 13 }p[NN]; 14 int n,m,q,x,M,opt,head[NN],num,c[NN],ans,maxr,l[10005],r[10005]; 15 int has[NN],ext,pre[NN],ts[NN]; 16 inline bool cmp(edge a,edge b){ return a.w<b.w; } 17 inline int getfa(int a){ return p[a].fa==a?a:p[a].fa=getfa(p[a].fa); } 18 inline int read(){ 19 int x=0,f=1; 20 char ch=getchar(); 21 while(ch<'0'||ch>'9'){ 22 if(ch=='-') f=-1; 23 ch=getchar(); 24 } 25 while(ch>='0'&&ch<='9'){ 26 x=(x<<1)+(x<<3)+(ch^48); 27 ch=getchar(); 28 } 29 return x*f; 30 } 31 void write(int x){ 32 if(x<0) putchar('-'), x=-x; 33 if(x>9) write(x/10); 34 putchar(x%10+'0'); 35 } 36 void lsh(){ 37 for(int i=1;i<=m;i++) has[i]=e[i].w; 38 ext=unique(has+1,has+1+m)-has-1; 39 for(int i=1;i<=m;i++) 40 e[i].w=lower_bound(has+1,has+ext+1,e[i].w)-has; 41 } 42 inline void add(int a,int b,int d){ 43 e[++num].to=b; e[num].nex=head[a]; head[a]=num; e[num].w=d; e[num].st=a; 44 } 45 void gather(int a,int b){ 46 a=getfa(a); b=getfa(b); 47 if(a==b) return; 48 p[b].fa=a; p[a].gl|=p[b].gl; 49 } 50 signed main(){ 51 n=read(); m=read(); q=read(); x=read(); opt=read(); ts[0]=1; 52 if(opt) M=read(); 53 for(rin i=1;i<=n;i++){ 54 c[i]=read(); 55 p[i].fa=i; p[i].gl[c[i]]=1; 56 } 57 for(rin i=1;i<=m;i++){ 58 int u=read(),v=read(),w=read(); 59 add(u,v,w); 60 } sort(e+1,e+m+1,cmp); 61 lsh(); 62 for(rin i=1;i<=m;i++){ 63 int u=e[i].st,v=e[i].to; 64 gather(u,v); 65 if(e[i].w!=e[i+1].w){ 66 ts[e[i].w]=p[getfa(x)].gl.count(); 67 pre[e[i].w]=pre[e[i].w-1]+ts[e[i].w-1]*(has[e[i].w]-has[e[i].w-1]-1); 68 pre[e[i].w]+=ts[e[i].w]; 69 } 70 } 71 for(rin i=1;i<=q;i++){ 72 int l=read(),r=read(); 73 if(opt) l=(l^ans)%M+1, r=(r^ans)%M+1; 74 if(l>r) swap(l,r); l--; 75 int wl=upper_bound(has,has+ext+1,l)-has-1; 76 int wr=upper_bound(has,has+ext+1,r)-has-1; 77 ans=pre[wr]+ts[wr]*(r-has[wr]); 78 ans-=pre[wl]+ts[wl]*(l-has[wl]); 79 write(ans); putchar('\n'); 80 } 81 return 0; 82 }
T2 牛半仙的妹子tree
考场暴力炸了,只有10pts。
题解里正解好像是分块,好像还能点分树,机房里还看到了很多奇奇怪怪的AC代码,
但最快的竟然是求LCA暴力判断就离谱。
记录染色的点和染色的时间,查询时枚举比较时间差和距离即可。
树剖竟然只400多ms?
code:
1 #include<bits/stdc++.h> 2 #define debug exit(0) 3 #define ii pair<int,int> 4 #define mp make_pair 5 #define fi first 6 #define se second 7 #define pb push_back 8 using namespace std; 9 const int NN=1e5+5; 10 int n,m,x,to[NN<<1],nex[NN<<1],head[NN],opt,num; 11 int dep[NN],son[NN],siz[NN],dfn[NN],id[NN],fa[NN],top[NN],cnt; 12 vector<ii>t; 13 inline int read(){ 14 int x=0,f=1; 15 char ch=getchar(); 16 while(ch<'0'||ch>'9'){ 17 if(ch=='-') f=-1; 18 ch=getchar(); 19 } 20 while(ch>='0'&&ch<='9'){ 21 x=(x<<1)+(x<<3)+(ch^48); 22 ch=getchar(); 23 } 24 return x*f; 25 } 26 void write(int x){ 27 if(x<0) putchar('-'), x=-x; 28 if(x>9) write(x/10); 29 putchar(x%10+'0'); 30 } 31 inline void add(int a,int b){ 32 to[++num]=b; nex[num]=head[a]; head[a]=num; 33 to[++num]=a; nex[num]=head[b]; head[b]=num; 34 } 35 void dfs1(int f,int s){ 36 fa[s]=f; dep[s]=dep[f]+1; siz[s]=1; 37 for(int i=head[s];i;i=nex[i]){ 38 int v=to[i]; 39 if(v==f) continue; 40 dfs1(s,v); 41 siz[s]+=siz[v]; 42 if(siz[v]>siz[son[s]]) son[s]=v; 43 } 44 } 45 void dfs2(int s,int t){ 46 top[s]=t; dfn[s]=++cnt; id[cnt]=s; 47 if(!son[s]) return; 48 dfs2(son[s],t); 49 for(int i=head[s];i;i=nex[i]){ 50 int v=to[i]; 51 if(v!=son[s]&&v!=fa[s]) dfs2(v,v); 52 } 53 } 54 inline int LCA(int x,int y){ 55 int fx=top[x],fy=top[y]; 56 while(fx!=fy) 57 if(dep[fx]<dep[fy]) y=fa[fy], fy=top[y]; 58 else x=fa[fx], fx=top[x]; 59 return dep[x]>dep[y]?y:x; 60 } 61 inline bool check(int x,int ti){ 62 for(int j=0;j<t.size();j++) 63 if(dep[x]+dep[t[j].fi]-2*dep[LCA(x,t[j].fi)]<=ti-t[j].se) 64 return 0; 65 return 1; 66 } 67 signed main(){ 68 n=read(); m=read(); 69 for(int i=1;i<n;i++) add(read(),read()); 70 dfs1(0,1); dfs2(1,1); 71 for(int i=1;i<=m;i++){ 72 opt=read(); x=read(); 73 if(opt==1) if(check(x,i)) t.pb(mp(x,i)); 74 if(opt==2) t.clear(); 75 if(opt==3) puts(check(x,i)?"orzFsYo":"wrxcsd"); 76 } 77 return 0; 78 }
upd:一个链一个菊花直接把RMQ跟树剖卡废了。考虑赵sir的神仙线段树。
一个点u被v染色,要满足$dep_{u}+dep_{v}-2\times dep_{lca_{u,v}}<=now-time_{v}$,移个项可得$time_{v}+dep_{v}-2\times dep_{lca_{u,v}}<=now-dep_{u}$。
在线段树中维护等式左边的最小值,每次修改向上修改到根,查询时也跳链比较等式左右即可。清空直接打标记。
code:
1 #include<bits/stdc++.h> 2 #define debug exit(0) 3 #define ld rt<<1 4 #define rd (rt<<1)|1 5 using namespace std; 6 const int NN=1e5+5,inf=2e9; 7 int n,m,x,to[NN<<1],nex[NN<<1],head[NN],opt,num; 8 int dep[NN],son[NN],siz[NN],dfn[NN],id[NN],fa[NN],top[NN],cnt; 9 inline int read(){ 10 int x=0,f=1; 11 char ch=getchar(); 12 while(ch<'0'||ch>'9'){ 13 if(ch=='-') f=-1; 14 ch=getchar(); 15 } 16 while(ch>='0'&&ch<='9'){ 17 x=(x<<1)+(x<<3)+(ch^48); 18 ch=getchar(); 19 } 20 return x*f; 21 } 22 void write(int x){ 23 if(x<0) putchar('-'), x=-x; 24 if(x>9) write(x/10); 25 putchar(x%10+'0'); 26 } 27 inline void add(int a,int b){ 28 to[++num]=b; nex[num]=head[a]; head[a]=num; 29 to[++num]=a; nex[num]=head[b]; head[b]=num; 30 } 31 void dfs1(int f,int s){ 32 fa[s]=f; dep[s]=dep[f]+1; siz[s]=1; 33 for(int i=head[s];i;i=nex[i]){ 34 int v=to[i]; 35 if(v==f) continue; 36 dfs1(s,v); 37 siz[s]+=siz[v]; 38 if(siz[v]>siz[son[s]]) son[s]=v; 39 } 40 } 41 void dfs2(int s,int t){ 42 top[s]=t; dfn[s]=++cnt; id[cnt]=s; 43 if(!son[s]) return; 44 dfs2(son[s],t); 45 for(int i=head[s];i;i=nex[i]){ 46 int v=to[i]; 47 if(v!=son[s]&&v!=fa[s]) dfs2(v,v); 48 } 49 } 50 struct segment_tree{ 51 int l[NN<<2],r[NN<<2],mn[NN<<2],laz[NN<<2],lzc[NN<<2]; 52 void pushup(int rt){ 53 if(l[rt]==r[rt]) return; 54 mn[rt]=min(mn[ld],mn[rd]); 55 } 56 void pushdown(int rt){ 57 if(l[rt]==r[rt]) return; 58 if(lzc[rt]){ 59 mn[ld]=inf; mn[rd]=inf; 60 laz[ld]=laz[rd]=0; 61 lzc[ld]=lzc[rd]=1; lzc[rt]=0; 62 } 63 if(laz[rt]){ 64 mn[ld]=min(mn[ld],laz[rt]-2*dep[id[r[ld]]]); 65 mn[rd]=min(mn[rd],laz[rt]-2*dep[id[r[rd]]]); 66 if(!laz[ld]) laz[ld]=laz[rt]; 67 else laz[ld]=min(laz[ld],laz[rt]); 68 if(!laz[rd]) laz[rd]=laz[rt]; 69 else laz[rd]=min(laz[rd],laz[rt]); 70 laz[rt]=0; 71 } 72 } 73 void build(int rt,int opl,int opr){ 74 l[rt]=opl; r[rt]=opr; mn[rt]=inf; 75 if(opl==opr) return; 76 int mid=opl+opr>>1; 77 build(ld,opl,mid); build(rd,mid+1,opr); 78 } 79 void update(int rt,int opl,int opr,int val){ 80 if(l[rt]>=opl&&r[rt]<=opr){ 81 if(val!=inf){ 82 mn[rt]=min(mn[rt],val-2*dep[id[r[rt]]]); 83 if(!laz[rt]) laz[rt]=val; 84 else laz[rt]=min(laz[rt],val); 85 } 86 else{ mn[rt]=inf; laz[rt]=0; lzc[rt]=1; } 87 return; 88 } 89 pushdown(rt); 90 int mid=l[rt]+r[rt]>>1; 91 if(opl<=mid) update(ld,opl,opr,val); 92 if(opr>mid) update(rd,opl,opr,val); 93 pushup(rt); 94 } 95 int query(int rt,int opl,int opr){ 96 if(l[rt]>=opl&&r[rt]<=opr) return mn[rt]; 97 pushdown(rt); 98 int mid=l[rt]+r[rt]>>1,ans=inf; 99 if(opl<=mid) ans=min(ans,query(ld,opl,opr)); 100 if(opr>mid) ans=min(ans,query(rd,opl,opr)); 101 return ans; 102 } 103 }s; 104 void UPD(int x,int val){ 105 int fx=top[x]; 106 val=val+dep[x]; 107 while(fx!=1){ 108 s.update(1,dfn[fx],dfn[x],val); 109 x=fa[fx]; fx=top[x]; 110 } 111 s.update(1,1,dfn[x],val); 112 } 113 void SOL(int x,int val){ 114 int fx=top[x]; 115 val=val-dep[x]; 116 while(fx!=1){ 117 if(s.query(1,dfn[fx],dfn[x])<=val){ 118 puts("wrxcsd"); 119 return; 120 } 121 x=fa[fx]; fx=top[x]; 122 } 123 if(s.query(1,1,dfn[x])<=val) puts("wrxcsd"); 124 else puts("orzFsYo"); 125 } 126 int main(){ 127 n=read(); m=read(); 128 for(int i=1;i<n;i++) add(read(),read()); 129 dfs1(0,1); dfs2(1,1); s.build(1,1,n); 130 for(int i=1;i<=m;i++){ 131 opt=read(); x=read(); 132 if(opt==1) UPD(x,i); 133 if(opt==2) s.update(1,1,n,inf); 134 if(opt==3) SOL(x,i); 135 } 136 return 0; 137 }
T3 牛半仙的妹子序列
一眼极长上升子序列。
但不会。
于是又回顾了一遍god knows(模拟16),还是不大懂,整了挺久。但好像几乎一模一样。
把求最小权的步骤换成求方案数就完了。
code:
1 #include<bits/stdc++.h> 2 #define debug exit(0) 3 #define int long long 4 #define ld rt<<1 5 #define rd (rt<<1)|1 6 using namespace std; 7 const int p=998244353,NN=2e5+5; 8 int n,bty[NN],ans; 9 inline int read(){ 10 int x=0,f=1; 11 char ch=getchar(); 12 while(ch<'0'||ch>'9'){ 13 if(ch=='-') f=-1; 14 ch=getchar(); 15 } 16 while(ch>='0'&&ch<='9'){ 17 x=(x<<1)+(x<<3)+(ch^48); 18 ch=getchar(); 19 } 20 return x*f; 21 } 22 void write(int x){ 23 if(x<0) putchar('-'), x=-x; 24 if(x>9) write(x/10); 25 putchar(x%10+'0'); 26 } 27 struct segment_tree{ 28 int mx[NN<<2],sum[NN<<2],l[NN<<2],r[NN<<2],us[NN<<2],nxt; 29 int calc(int rt,int val){ 30 if(l[rt]==r[rt]) return mx[rt]>val?sum[rt]:0; 31 if(mx[rd]<=val) return calc(ld,val); 32 return (us[ld]+calc(rd,val))%p; 33 } 34 void pushup(int rt){ 35 mx[rt]=max(mx[ld],mx[rd]); 36 sum[rt]=(sum[rd]+(us[ld]=calc(ld,mx[rd])))%p; 37 } 38 void build(int rt,int opl,int opr){ 39 l[rt]=opl; r[rt]=opr; mx[rt]=-1; 40 if(opl==opr) return; 41 int mid=opl+opr>>1; 42 build(ld,opl,mid); 43 build(rd,mid+1,opr); 44 } 45 void insert(int rt,int pos,int i,int val){ 46 if(l[rt]==r[rt]){ sum[rt]=val; mx[rt]=i; return; } 47 int mid=l[rt]+r[rt]>>1; 48 if(pos<=mid) insert(ld,pos,i,val); 49 else insert(rd,pos,i,val); 50 pushup(rt); 51 } 52 int query(int rt,int opl,int opr){ 53 if(l[rt]>=opl&&r[rt]<=opr){ 54 int ans=calc(rt,nxt); 55 nxt=max(nxt,mx[rt]); 56 return ans; 57 } 58 int mid=l[rt]+r[rt]>>1,ans=0; 59 if(opr>mid) (ans+=query(rd,opl,opr))%=p; 60 if(opl<=mid) (ans+=query(ld,opl,opr))%=p; 61 return ans; 62 } 63 }s; 64 signed main(){ 65 n=read(); 66 for(int i=1;i<=n;i++) bty[i]=read(); 67 ++n; bty[n]=n; s.build(1,0,n); s.insert(1,0,0,0); 68 for(int i=1;i<=n;i++){ 69 s.nxt=-1; 70 int tmp=s.query(1,0,bty[i]-1)%p; 71 if(!tmp) tmp=1; 72 s.insert(1,bty[i],i,tmp); 73 if(i==n) write(tmp), putchar('\n'); 74 } 75 return 0; 76 }