2018CCPC女生赛(树上莫队)
签到题这里久懒得写了。
B - 缺失的数据范围
Total Submission(s): 2602 Accepted Submission(s): 559
题意:求最大的N,满足N^a*[log2(N)]^b<=K;
思路:二分即可,log2要手写,然后就是注意判pow是否超过long long。
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1000100; const ll inf=1e18; ll A,B,K,ans,aa[61]; ll Log(ll x){ int pos=lower_bound(aa+1,aa+60+1,x)-aa; return pos; } bool check(ll x){ ll a=1,res=1; ll b=Log(x);// cout<<b<<" "<<aa[b]<<" "; for(int i=1;i<=A;i++){ if(a>K/x) return false; a=a*x; if(a>K) return false; } for(int i=1;i<=B;i++){ if(res>K/b) return false; res=res*b; if(res>K) return false; } //cout<<a<<" "<<b<<endl; if(a>K/res) return false; if(a*res<=K) return true; } int main() { int T; aa[0]=1; for(int i=1;i<=60;i++) aa[i]=aa[i-1]*2; scanf("%d",&T); while(T--){ scanf("%lld%lld%lld",&A,&B,&K); ans=0; ll L=1,R=K; while(L<=R){ ll Mid=(L+R)/2; if(check(Mid)) ans=Mid,L=Mid+1; else R=Mid-1; } printf("%lld\n",ans); } return 0; }
E - 对称数
Total Submission(s): 469 Accepted Submission(s): 88
题意:给出一棵带点权的树,Q次询问,每次询问给出(u,v),求这个路径上最小的出现次数位偶数的正整数。
思路:树上莫队:皇室联邦法分块,括号法移动区间。
皇室联邦法分块:即按照DFS虚分块。
括号法:dfs时,记录第一次访问时间戳in[]和最后一次访问时间戳out[]。如果访问路径(u,v),保证in[u]<in[v]起对应的区间就是:
1,LCA==u,对应[in[u],in[v]];
2,LCA!=u,对应[out[u],in[v]]+LCA;LCA单独考虑为pre,不要忽略。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=400010; int a[maxn],Laxt[maxn],Next[maxn],To[maxn],cnt,B; int g[maxn],dep[maxn],fa[maxn][20],ans[maxn],scc,tot; int in[maxn],out[maxn],p[maxn]; bitset<maxn>Set; struct in{ int u,v,id; friend bool operator <(in ww,in vv){ if(g[ww.u]==g[vv.u]) return g[ww.v]<g[vv.v]; return g[ww.u]<g[vv.u]; } }s[maxn]; void add(int u,int v) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void dfs(int u,int f) { fa[u][0]=f; dep[u]=dep[f]+1; if(tot%B==0) scc++; g[u]=scc; in[u]=++tot; p[tot]=a[u]; for(int i=Laxt[u];i;i=Next[i]){ if(To[i]!=f) dfs(To[i],u); } out[u]=++tot; p[tot]=a[u]; } int LCA(int u,int v) { if(dep[u]<dep[v]) swap(u,v); for(int i=18;i>=0;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if(u==v) return u; for(int i=18;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } int main() { int T,N,M,u,v,Lca; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); B=sqrt(N); cnt=0; rep(i,1,N) Laxt[i]=0; rep(i,1,N) scanf("%d",&a[i]); rep(i,1,N-1){ scanf("%d%d",&u,&v); add(u,v); add(v,u); } tot=0; scc=0; dfs(1,0); rep(i,1,18) rep(j,1,N) fa[j][i]=fa[fa[j][i-1]][i-1]; rep(i,1,M) scanf("%d%d",&s[i].u,&s[i].v),s[i].id=i; rep(i,1,M) if(in[s[i].u]>in[s[i].v]) swap(s[i].u,s[i].v); sort(s+1,s+M+1); Set.set(); int L=1,R=1,pre=0; Set.flip(p[1]); rep(i,1,M) { Set.flip(pre); int Lca=LCA(s[i].u,s[i].v),l,r; if(Lca==s[i].u) l=in[s[i].u],r=in[s[i].v],pre=0; else l=out[s[i].u],r=in[s[i].v],Set.flip(a[Lca]),pre=a[Lca]; while(l<L) Set.flip(p[--L]); while(l>L) Set.flip(p[L++]); while(r>R) Set.flip(p[++R]); while(r<R) Set.flip(p[R--]); int pos=Set._Find_next(0); ans[s[i].id]=pos; } rep(i,1,M) printf("%d\n",ans[i]); } return 0; }
I - 回文树
题意:给出一棵带权树,点权随机给出,求树上有多少回文串。
思路:由于是随机,我们大胆猜测,只存在长度为1,2和3的回文串。
Total Submission(s): 198 Accepted Submission(s): 45
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100010; int a[maxn]; map<int,int>mp[maxn]; int main() { int N,T,u,v; scanf("%d",&T); while(T--){ int ans=0; scanf("%d",&N); rep(i,1,N) mp[i].clear(); rep(i,1,N) scanf("%d",&a[i]); rep(i,1,N-1){ scanf("%d%d",&u,&v); if(a[u]==a[v]) ans++; ans+=mp[u][a[v]]; ans+=mp[v][a[u]]; mp[u][a[v]]++; mp[v][a[u]]++; } printf("%d\n",ans+N); } return 0; }
It is your time to fight!