100道codeforces2100分(施工中)
考虑这些被释放的,值一定相同,并且等于区间gcd 于是用st表询问区间gcd,map套二分实现区间里某个数字出现次数 int n,a[100010]; int f[100010][20],lgn[100010]; map<int,vector<int>>pos; int ask(int l,int r) { int s=lgn[r-l+1]; return __gcd(f[l][s],f[r-(1<<s)+1][s]); } int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++){ f[i][0]=a[i]=read(); pos[a[i]].push_back(i); } lgn[1]=0; for(int i=2;i<=n;i++) lgn[i]=lgn[i/2]+1; int logn=lgn[n]; for(int j=1;j<=logn;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=__gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]); for(int m=read();m;m--) { int l=read(),r=read(); int gcd=ask(l,r); if(lower_bound(pos[gcd].begin(),pos[gcd].end(),l)==pos[gcd].end()) cout<<r-l+1<<"\n"; else cout<<r-l+1-(upper_bound(pos[gcd].begin(),pos[gcd].end(),r)-lower_bound(pos[gcd].begin(),pos[gcd].end(),l))<<'\n'; } }
注意到确定一个起点后,这棵树的答案就是固定的了,是Σd[i],其中d[x]表示到起点的路径上的点的数量 用换根dp维护之 具体地,第一遍dfs得到初始的d数组和f数组,f[i]表示令1为根,以i为根的子树的Σd[x] 第二遍dfs维护上面的点距离x的距离之和与上面的点的数量,往下走的时候修改一下即可 int n; vector<int>e[200010]; ll f[200010],ans,d[200010],cnt[200010]; void dfs(int x) { f[x]=d[x]; cnt[x]=1; for(auto y:e[x]){ if(d[y]) continue; d[y]=d[x]+1; dfs(y); f[x]=f[x]+f[y]; cnt[x]+=cnt[y]; } } void dfs1(int x,ll sum,int num) { ans=max(ans,f[x]-(d[x]-1)*cnt[x]+sum); for(auto y:e[x]) { if(d[y]<d[x]) continue; int numm=cnt[x]-cnt[y];//子树里除了y的子树还剩几个点 dfs1(y,sum+num+f[x]-f[y]-(d[x]-2)*numm,num+numm); } } int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } d[1]=1; dfs(1); dfs1(1,0,0); cout<<ans; }
考虑二分答案 对于mid,考虑枚举ai,则需要检查[k*a[i]+x,k*a[i]+a[i]-1]这个区间里有没有数字 复杂度nlog^2 不二分答案也行,考虑枚举ai,查找[k*a[i],(k+1)*a[i])区间里最后一个出现的数字,更新答案,复杂度仍然是nlog^2 int n,a[200010],c[1000010]; int check(int x) { for(int i=1;i<=n;i++) for(int r=min(1000000,a[i]*2-1),l=a[i]+x;l<=r;l+=a[i],r=min(1000000,r+a[i])) if(c[r]-c[l-1]) return 1; return 0; } int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+1+n); n=unique(a+1,a+1+n)-a-1; for(int i=1;i<=n;i++) c[a[i]]=1; for(int i=1;i<=1000000;i++) c[i]+=c[i-1]; int l=1,r=1000000,mid; while(l+1<r) { mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } if(check(r)) cout<<r; else if(check(l)) cout<<l; else cout<<0; }
点分治板子 考虑1 2 3 4 5 6 7这个链应该怎么做 可以发现是4最高,26次之,1357最低 于是点分治的过程中给分治的那个点赋值即可 int vis[100010],siz[100010],wt[100010]; int n,Root,Tsiz,now='A'-1; char ans[100010]; vector<int>e[100010]; void getroot(int x,int f) { siz[x]=1; wt[x]=0; for(auto y:e[x]) { if(y==f||vis[y]) continue; getroot(y,x); siz[x]+=siz[y]; wt[x]=max(wt[x],siz[y]); } wt[x]=max(wt[x],Tsiz-siz[x]); if(wt[Root]>wt[x]) Root=x; } void DFS(int x) { now++; ans[x]=now; vis[x]=1; for(auto y:e[x]) { if(vis[y]) continue; Root=0; Tsiz=siz[y]; getroot(y,0); DFS(Root); } now--; } int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } wt[Root=0]=0x3f3f3f3f; Tsiz=n; getroot(1,0); ans[Root]=now; DFS(Root); for(int i=1;i<=n;i++) cout<<ans[i]<<' '; }
先跑一个最小生成树 对于边xyw 如果原本就被取用了,当然输出最小生成树的权值和 否则,考虑在最小生成树上跑lca来求xy路径上的最大边权,用w来替代之,输出新的边权和 int n,m,fa[200010],f[200010][20],maxx[200010][20],d[200010]; ll sum; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } struct edge { int i,x,y,w,flag; }o[200010]; bool w_(edge a,edge b) { return a.w<b.w; } bool i_(edge a,edge b) { return a.i<b.i; } vector<pair<int,int>>e[200010]; void dfs(int x) { for(int i=0;i<e[x].size();i++) { int y=e[x][i].first,w=e[x][i].second; if(d[y]) continue; d[y]=d[x]+1; f[y][0]=x; maxx[y][0]=w; dfs(y); } } int lca(int x,int y) { int t=0; if(d[x]<d[y]) swap(x,y); for(int i=19;i>=0;i--) if(d[f[x][i]]>=d[y]) { t=max(t,maxx[x][i]); x=f[x][i]; } if(x==y) return t; for(int i=19;i>=0;i--) { if(f[x][i]!=f[y][i]) { t=max(t,maxx[x][i]); t=max(t,maxx[y][i]); x=f[x][i]; y=f[y][i]; } } return max(max(maxx[x][0],maxx[y][0]),t); } int main() { // freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) { o[i].i=i; o[i].x=read(); o[i].y=read(); o[i].w=read(); } sort(o+1,o+1+m,w_); for(int i=1;i<=m;i++) { if(get(o[i].x)==get(o[i].y)) continue; e[o[i].x].push_back({o[i].y,o[i].w}); e[o[i].y].push_back({o[i].x,o[i].w}); sum=sum+o[i].w; o[i].flag=1; fa[fa[o[i].x]]=fa[o[i].y]; } sort(o+1,o+1+m,i_); d[1]=1; dfs(1); for(int i=1;i<20;i++) for(int x=1;x<=n;x++) { f[x][i]=f[f[x][i-1]][i-1]; maxx[x][i]=max(maxx[x][i-1],maxx[f[x][i-1]][i-1]); } for(int i=1;i<=m;i++) if(o[i].flag) printf("%lld\n",sum); else printf("%lld\n",sum-lca(o[i].x,o[i].y)+o[i].w); }
考虑二分答案x 把如果ai大于等于x,看做1,如果小于x,看做0,赋值给bi 于是问题转变成有没有长大于等于k的区间,满足区间和大于0 于是前缀和,问题转变成对于前缀和bi,在它k个之后,有没有大于他的 int n,k,a[200010],b[200010]; multiset<int>o; int check(int x) { o.clear(); for(int i=1;i<=n;i++) { if(a[i]<x) b[i]=b[i-1]-1; else b[i]=b[i-1]+1; if(i>=k) o.insert(b[i]); } for(int l=0,r=k;r<=n;l++,r++) { if(*o.rbegin()>b[l]) return 1; o.erase(o.find(b[r])); } return 0; } int main() { // freopen("1.in","r",stdin); n=read();k=read(); for(int i=1;i<=n;i++) a[i]=read(); int l=1,r=200000,mid; while(l+1<r) { mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } if(check(r)) cout<<r; else cout<<l; }
着急吃饭导致wa了两发 实现一个倍增求lca,和一个在树上往上跳若干次,返回跳到哪里的函数 于是分类讨论 如果xy距离是奇数输出0 如果x=y输出n 如果d[x]=d[y],则把x跳到lca下面,y跳到lca下面,输出n-cnt[x]-cnt[y] 否则,把z跳到dis/2位置,x跳到z下面,输出cnt[z]-cnt[x] int n,d[100010],f[100010][20],cnt[100010]; vector<int>e[100010]; int lca(int x,int y) { if(d[x]<d[y]) swap(x,y); for(int i=19;i>=0;i--) if(d[f[x][i]]>=d[y]) x=f[x][i]; if(x==y) return x; for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } return f[x][0]; } void dfs(int x) { cnt[x]=1; for(auto y:e[x]) { if(d[y]) continue; d[y]=d[x]+1; f[y][0]=x; dfs(y); cnt[x]+=cnt[y]; } } int ask(int x,int d) { for(int i=19;i>=0;i--) { if(d>=(1<<i)){ x=f[x][i]; d-=(1<<i); } } return x; } int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } d[1]=1; dfs(1); for(int i=1;i<20;i++) for(int x=1;x<=n;x++) f[x][i]=f[f[x][i-1]][i-1]; for(int m=read();m;m--) { int x=read(),y=read(); if(x==y) { cout<<n<<'\n'; continue; } if(d[x]<d[y]) swap(x,y); int z=lca(x,y),dis=d[x]+d[y]-2*d[z]; if(dis&1) cout<<"0\n"; else if(d[x]==d[y]) { z=ask(x,dis/2); x=ask(x,dis/2-1); y=ask(y,dis/2-1); cout<<n-cnt[x]-cnt[y]<<'\n'; } else { z=ask(x,dis/2); x=ask(x,dis/2-1); cout<<cnt[z]-cnt[x]<<'\n'; } } }
首先注意到,x的兄弟们一定和他深度相同 那这个p什么用处呢,他规定了询问都有p次祖先的兄弟们 于是跑一跑dfs序,预处理倍增,令y是x的p次祖先 被询问的兄弟就是y的子树里,和x同一深度的点的数量。也就是dfs序处于[l[y],r[y]]之间的深度是d[x]的点的数量 用vector维护之 int tot,l[100010],r[100010],f[100010][20],dfn[100010],d[100010]; vector<int>o[100010],e[100010]; void dfs(int x) { tot++; l[x]=r[x]=dfn[x]=tot; o[d[x]].push_back(dfn[x]); for(auto y:e[x]) { d[y]=d[x]+1; f[y][0]=x; dfs(y); r[x]=r[y]; } } int ask(int x,int t) { for(int i=19;i>=0;i--) if(t>=(1<<i)){ t-=(1<<i); x=f[x][i]; } return x; } int main() { // freopen("1.in","r",stdin); int n=read(); for(int i=1;i<=n;i++) e[read()].push_back(i); for(auto y:e[0]) { d[y]=1; dfs(y); } for(int i=1;i<=19;i++) for(int x=1;x<=n;x++) f[x][i]=f[f[x][i-1]][i-1]; for(int m=read();m;m--) { int x=read(),p=read(); int y=ask(x,p); if(y==0) cout<<0<<' '; else cout<<upper_bound(o[d[x]].begin(),o[d[x]].end(),r[y])-lower_bound(o[d[x]].begin(),o[d[x]].end(),l[y])-1<<' '; } }
是不是走错地方了,2100显然比2000简单的多 考虑dp,对于每一行,01背包,f[i][j]表示选了i个数字,数字之和%k=j的最大数字之和 每行的求出来之后,再更新g[i][j]表示前i行数字之和%k=j的最大数字之和,这里是分组背包 n,m只有70,大力n^4做即可 int n,m,k,a[71],f[71][71],g[71][71]; void work() { memset(f,0xef,sizeof(f)); f[0][0]=0; for(int i=1;i<=m;i++) { a[i]=read(); for(int cnt=i-1;cnt>=0;cnt--) for(int j=0;j<k;j++) f[cnt+1][(j+a[i])%k]=max(f[cnt+1][(j+a[i])%k],f[cnt][j]+a[i]); } } int main() { // freopen("1.in","r",stdin); n=read();m=read();k=read(); memset(g,0xef,sizeof(g)); g[0][0]=0; for(int i=1;i<=n;i++) { for(int x=0;x<k;x++) g[i][x]=max(g[i][x],g[i-1][x]); work(); for(int cnt=1;cnt<=m/2;cnt++) for(int x=0;x<k;x++) { g[i][x]=max(g[i][x],g[i-1][x]); for(int y=0;y<k;y++) g[i][(x+y)%k]=max(g[i][(x+y)%k],f[cnt][y]+g[i-1][x]); } } cout<<g[n][0]; }
考虑从每个点bfs,每次从set里面挑点,如果和x有连边就是同一并查集,否则continue 复杂度只有(m+n)log int n,m,vis[200010]; set<int>e[200010],o; queue<int>q; priority_queue<int,vector<int>,greater<int>>ans; void bfs(int x) { q.push(x); o.erase(x); int cnt=0; while(q.size()) { x=q.front(); q.pop(); if(vis[x])continue; vis[x]=1; cnt++; for(auto i=o.begin();i!=o.end();) { int y=*i; i++; if(e[x].find(y)==e[x].end()) { q.push(y); o.erase(y); } } } ans.push(cnt); } int main() { // freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++) o.insert(i); for(int i=1;i<=m;i++) { int x=read(),y=read(); e[x].insert(y); e[y].insert(x); } for(int i=1;i<=n;i++) { if(vis[i]) continue; bfs(i); } cout<<ans.size()<<'\n'; while(ans.size()) { cout<<ans.top()<<' '; ans.pop(); } }
考虑区间dp f[l][r]表示区间lr最少变成几个数字 f[l][r]=min(f[l][m]+f[m+1][r]) 如果l,m和m+1,r都能变成一个数字,我们用v数组记录他变成了哪个数字,如果v也相同,flr就可以=1 int f[510][510]; int n,a[510],v[510][510]; int main() { // freopen("1.in","r",stdin); n=read(); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++) { a[i]=read(); v[i][i]=a[i]; f[i][i]=1; } for(int len=2;len<=n;len++) for(int l=1,r=len;r<=n;l++,r++) for(int m=l;m<r;m++) if(f[l][m]==1&&f[m+1][r]==1&&v[l][m]==v[m+1][r]) { f[l][r]=1,v[l][r]=v[l][m]+1; break; } else f[l][r]=min(f[l][r],f[l][m]+f[m+1][r]); cout<<f[1][n]; }
如果已知gcd(a,b)和lcm(a,b),令t表示lcm(a,b)/gcd(a,b)的质因子种类数 则a和b的不同方案数是2的t次方 考虑问的是c*lcm-d*gcd=x,不妨令xcd除以gcd(x,c,d) 然后令a=agcd,b=bgcd,则lcm=abgcd cab*gcd=d*gcd=x 这要求x整除gcd,可以枚举,复杂度t根号x cab=x/gcd+d,这要求x/gcd+d整除c,可以模一下判断一下 ab=(x/gcd+d)/c 问题转变成互质的ab,已知乘积 只需要求出乘积的质因子种类数,求2的他的幂次就好了 那么问题转变成o1询问某个数字的质因子种类数 考虑用埃氏筛预处理一下,跑0.5s即可 int f[20000010]; ll ans,g[110]; ll c,d,x; void add(ll gcd) { ll t=x/gcd+d; if(t%c) return ; t=t/c; ans=ans+g[f[t]]; } void work() { ans=0; c=read(),d=read(),x=read(); ll t=__gcd(__gcd(c,d),x); c=c/t;d=d/t;x=x/t; for(int gcd=1;gcd*gcd<=x;gcd++) { if(x%gcd) continue; add(gcd); if(x/gcd!=gcd) add(x/gcd); } cout<<ans<<'\n'; } int main() { // freopen("1.in","r",stdin); for(int i=2;i<=20000000;i++) { if(f[i])continue; for(int j=i;j<=20000000;j+=i) f[j]++; } g[0]=1; for(int i=1;i<=100;i++) g[i]=g[i-1]*2; for(int t=read();t;t--) work(); }
手玩一下n=5和n=7,可以发现一定是选2个相邻的,剩下的隔一个选一个 于是枚举相邻的是哪个,隔一个选一个可以用前缀和o1求出来 int n,a[200010]; ll ans,c[200010]; int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++) a[i]=read(); c[1]=a[1]; for(int i=2;i<=n;i++) c[i]=c[i-2]+a[i]; ans=c[n]; for(int i=1;i<n;i++) ans=max(ans,c[i]+c[n-i%2]-c[i-1]); cout<<ans; }
构造题 考虑斜着第一列,第四列,第七列。。。 和第二列,第五列,第八列。。。 和第三列,第六列,第九列。。。 在这三列里选,取min,总有至少一个小于等于sum/3 于是看看谁最小,用它即可 char s[1010][1010]; int n,dx[]={0,0,1,-1},dy[]={1,-1,0,0},cnt[10]; int ask(int x,int y) { if(x>n||y>n) return 0; for(int i=0;i<4;i++) { if(min(x+2*dx[i],y+2*dy[i])<1||max(x+2*dx[i],y+2*dy[i])>n) continue; if(s[x][y]=='X'&&s[x+dx[i]][y+dy[i]]=='X'&&s[x+2*dx[i]][y+2*dy[i]]=='X') return 1; } for(int i=0;i<4;i++) if(s[x][y]=='X'&&s[x+dx[i]][y+dy[i]]=='X'&&s[x-dx[i]][y-dy[i]]=='X') return 1; return 0; } void work() { n=read(); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int j=1;j<=3;j++) { cnt[j]=0; for(int i=j;i<=3*n;i+=3) for(int x=1,y=i;y;x++,y--) if(ask(x,y)) cnt[j]++; } int j; if(min(cnt[1],min(cnt[2],cnt[3]))==cnt[1]) j=1; else if(min(cnt[1],min(cnt[2],cnt[3]))==cnt[2]) j=2; else j=3; for(int i=j;i<=3*n;i+=3) for(int x=1,y=i;y;x++,y--) if(ask(x,y)) s[x][y]='O'; for(int i=1;i<=n;i++) printf("%s\n",s[i]+1); } int main() { // freopen("1.in","r",stdin); for(int t=read();t;t--) work(); }
数学,计算几何题 最多100边形,考虑枚举一下,判断夹角是否能够整除360/n struct node { double x,y; }A,B,C,D; double v1,v2,v3,r,v[5]; double ask(node a,node b,node c) { a.x-=c.x; a.y-=c.y; b.x-=c.x; b.y-=c.y; return acos((a.x*b.x+a.y*b.y)/r/r); } void work(int n) { double t=2*acos(-1)/n; for(int i=1;i<=3;i++) if( fabs(v[i]-round(v[i]/t)*t)>1e-5) return ; printf("%8lf",r*r*sin(t)*n/2); exit(0); } int main() { // freopen("1.in","r",stdin); cin>>A.x>>A.y>>B.x>>B.y>>C.x>>C.y; D.x=((B.y-A.y)*(C.y*C.y-A.y*A.y+C.x*C.x-A.x*A.x)-(C.y-A.y)*(B.y*B.y-A.y*A.y+B.x*B.x-A.x*A.x))/(2.0*((C.x-A.x)*(B.y-A.y)-(B.x-A.x)*(C.y-A.y))); D.y=((B.x-A.x)*(C.x*C.x-A.x*A.x+C.y*C.y-A.y*A.y)-(C.x-A.x)*(B.x*B.x-A.x*A.x+B.y*B.y-A.y*A.y))/(2.0*((C.y-A.y)*(B.x-A.x)-(B.y-A.y)*(C.x-A.x))); r=(A.x-D.x)*(A.x-D.x)+(A.y-D.y)*(A.y-D.y); r=sqrt(r); v[1]=ask(A,B,D); v[2]=ask(A,C,D); v[3]=ask(B,C,D); for(int n=3;n<=100;n++) work(n); }
树上dfs序相关 给v注水可以使用dfs序,线段树区间修改,再把区间里的set里的点删掉,替换为v的父亲 清空节点v,我们可以使用set插入 判断v是否装满水,我们先看看set里面有没有v的儿子,如果有,显然输出0。否则输出线段树单点询问的值即可。 #include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } int cnt,dfn[500010],l[500010],r[500010]; int n,lazy[2000010],fa[500010]; vector<int>e[500010]; set<int>o; void dfs(int x) { cnt++; dfn[x]=l[x]=r[x]=cnt; for(auto y:e[x]) { if(dfn[y]) continue; fa[y]=x; dfs(y); r[x]=r[y]; } } void build(int x,int l,int r) { lazy[x]=-1;//全是空的 if(l==r) return ; int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); } void add(int x,int l,int r,int tl,int tr) { if(lazy[x]==1) return; if(tl<=l&&r<=tr) { lazy[x]=1; return ; } int mid=(l+r)/2; if(tl<=mid) add(x*2,l,mid,tl,tr); if(tr>mid) add(x*2+1,mid+1,r,tl,tr); if(lazy[x*2]==1&&lazy[x*2+1]==1) lazy[x]=1; else lazy[x]=-1; } int ask(int x,int l,int r,int d) { if(lazy[x]==1) return 1; if(l==r) return 0; int mid=(l+r)/2; if(d<=mid) return ask(x*2,l,mid,d); else return ask(x*2+1,mid+1,r,d); } int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dfs(1); build(1,1,n); for(int q=read();q;q--) { int t=read(); if(t==1) { int x=read(); add(1,1,n,l[x],r[x]); while(1) { auto t=o.lower_bound(l[x]); if(t==o.end()||*t>r[x]) break; o.erase(t); if(x==1) continue; o.insert(dfn[fa[x]]); } } else if(t==2) o.insert(dfn[read()]); else { int x=read(); auto t=o.lower_bound(l[x]); if(t!=o.end()&&*t<=r[x]) { cout<<"0\n"; continue; } cout<<ask(1,1,n,dfn[x])<<'\n'; } } }
使用直径和并查集的知识解决本题,在并查集合并的途中更新直径。 因为两个联通块相连,为了使得新连通块直径最长,需要连接两个直径的中点,分类讨论一下,发现新的直径为t=max((len[x]+1)/2+(len[y]+1)/2+1,max(len[x],len[y])) int fa[300010],t,maxx,len[300010]; int n,m,q; vector<int>o,e[300010]; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } void dfs(int x,int beg,int d) { if(d>maxx){ t=x; maxx=d; } o.push_back(x); fa[x]=beg; for(auto y:e[x]) { if(fa[y]) continue; dfs(y,beg,d+1); } } int main() { // freopen("1.in","r",stdin); n=read();m=read();q=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } for(int i=1;i<=n;i++) { if(fa[i]) continue; maxx=0; t=i;; o.clear(); dfs(i,i,0); for(auto x:o) fa[x]=0; o.clear(); dfs(t,i,0); len[i]=maxx; } for(;q;q--) { if(read()==1) cout<<len[get(read())]<<'\n'; else { int x=get(read()); int y=get(read()); if(x==y) continue; int t=max((len[x]+1)/2+(len[y]+1)/2+1,max(len[x],len[y])); fa[x]=y; len[y]=t; } } }
树上多源bfs 既然原图是合法的,把每个点的管辖范围的交界处的边删掉即可。 int n,k,d,v[300010]; queue<int>q; map<int,int>o[300010]; vector<int>e[300010],ans; int main() { // freopen("1.in","r",stdin); n=read();k=read();d=read(); for(int i=1;i<=k;i++) { int x=read(); q.push(x); v[x]=x; } for(int i=1;i<n;i++) { int x=read(),y=read(); o[x][y]=i; e[x].push_back(y); e[y].push_back(x); } while(q.size()) { int x=q.front();q.pop(); for(auto y:e[x]) { if(v[y]) continue; v[y]=v[x]; q.push(y); } } for(int x=1;x<=n;x++) for(auto y:e[x]) if(v[x]!=v[y]&&o[x][y]) ans.push_back(o[x][y]); cout<<ans.size()<<'\n'; for(auto v:ans) cout<<v<<' '; }
跑一个dfs序,用一个long long来存储一个区间里每个颜色在区间里是否出现过,则区间修改使用线段树修改一下,区间询问用线段树区间询问一下。 ll a[1600010],lazy[1600010],c[400010]; int l[400010],r[400010],cnt; int n,m; vector<int>e[400010]; void pushdown(int x) { lazy[x*2]=lazy[x*2+1]=lazy[x]; a[x*2]=a[x*2+1]=lazy[x]; lazy[x]=0; } int count(ll x) { int sum=0; while(x) { sum++; x=x-(x&(-x)); } return sum; } void add(int x,int l,int r,int d,ll v) { a[x]|=v; if(l==r) return ; int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d,v); else add(x*2+1,mid+1,r,d,v); } void dfs(int x) { l[x]=r[x]=++cnt; add(1,1,n,cnt,c[x]); for(auto y:e[x]) { if(l[y]) continue; dfs(y); } r[x]=cnt; } void add(int x,int l,int r,int tl,int tr,ll v) { if(tl<=l&&r<=tr) { lazy[x]=a[x]=v; return ; } if(lazy[x]) pushdown(x); int mid=(l+r)/2; if(tl<=mid) add(x*2,l,mid,tl,tr,v); if(tr>mid) add(x*2+1,mid+1,r,tl,tr,v); a[x]=a[x*2]|a[x*2+1]; } ll ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return a[x]; if(lazy[x]) pushdown(x); int mid=(l+r)/2; ll t=0; if(tl<=mid) t=ask(x*2,l,mid,tl,tr); if(tr>mid) t|=ask(x*2+1,mid+1,r,tl,tr); return t; } int main() { // freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++) c[i]=(1ll<<(read())); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dfs(1); for(;m;m--) { if(read()&1) { int x=read(); add(1,1,n,l[x],r[x],1ll<<read()); } else { int x=read(); cout<<count(ask(1,1,n,l[x],r[x]))<<'\n'; } } }
有向图连通性,写一个tarjan类似物。 如果没有环,显然是一种颜色。否则把回边赋值为2号颜色,出边赋值为1号颜色 int col[5010],n,m,cy,ans[5010]; vector<pair<int,int>>e[5010]; void dfs(int x) { col[x]=1; for(auto it:e[x]) { int y=it.first,i=it.second; if(!col[y]) { dfs(y); ans[i]=1; } else if(col[y]==2) ans[i]=1; else { ans[i]=2; cy=1; } } col[x]=2; } int main() { // freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); e[x].push_back({y,i}); } for(int i=1;i<=n;i++) if(col[i]==0) dfs(i); cout<<cy+1<<'\n'; for(int i=1;i<=m;i++) cout<<ans[i]<<' '; }
能随意修改最短路的长度,考虑先求bfs最短路,然后枚举a到b和b到c的交叉点x,先走a到x,再走x到b,再走b到x,再走x到c。则b到x的部分会走两遍,贪心地让这段最短,其次让a到x的部分和x到c的部分最短。对所有的x取min即可 int n,m,a,b,c,d[200010][4],vis[200010][4],now; vector<int>e[200010]; ll p[200010],ans; queue<int>q; void bfs(int x) { vis[x][now]=1; q.push(x); while(q.size()) { x=q.front(); q.pop(); for(auto y:e[x]) { if(vis[y][now]) continue; d[y][now]=d[x][now]+1; vis[y][now]=1; q.push(y); } } } void work() { n=read();m=read();a=read();b=read();c=read(); for(int i=1;i<=n;i++) e[i].clear(); for(int i=1;i<=m;i++) p[i]=read(); sort(p+1,p+1+m); for(int i=1;i<=m;i++) p[i]=p[i-1]+p[i]; for(int i=1;i<=m;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } for(int i=1;i<=n;i++) vis[i][1]=vis[i][2]=vis[i][3]=0; now=1;d[a][1]=0; bfs(a); now=2;d[b][2]=0; bfs(b); now=3;d[c][3]=0; bfs(c); ll ans=1e18; for(int x=1;x<=n;x++) { if(d[x][1]+d[x][2]+d[x][3]>m) continue; ans=min(ans,p[d[x][1]+d[x][2]+d[x][3]]+p[d[x][2]]); } cout<<ans<<'\n'; } int main() { // freopen("1.in","r",stdin); for(int t=read();t;t--) work(); }
数学,stl 预处理一个vi表示i的最大质因子,则对修改的数字x进行质因子分解,复杂度是log的 考虑每次单点修改,对着位置i狠狠修改,顺便更新全局gcd即可。具体地,如果某个质因子的次数够了n次,则gcd乘上这个质因子,复杂度qlog^2 map<int,int>o[200010]; map<int,int>cnt[200010]; int n,q,a[200010],v[200010]; ll ans=1,mod=1e9+7; int main() { // freopen("1.in","r",stdin); for(int i=2;i<=200000;i++) { if(v[i])continue; for(int j=i;j<=200000;j+=i) v[j]=i; } n=read();q=read(); for(int i=1;i<=n;i++) { a[i]=read(); while(a[i]!=1) { o[i][v[a[i]]]++; cnt[v[a[i]]][o[i][v[a[i]]]]++; if(cnt[v[a[i]]][o[i][v[a[i]]]]==n) ans=ans*v[a[i]]%mod; a[i]=a[i]/v[a[i]]; } } for(;q;q--) { int i=read(); a[i]=read(); while(a[i]!=1) { o[i][v[a[i]]]++; cnt[v[a[i]]][o[i][v[a[i]]]]++; if(cnt[v[a[i]]][o[i][v[a[i]]]]==n) ans=ans*v[a[i]]%mod; a[i]=a[i]/v[a[i]]; } cout<<ans<<'\n'; } }
注意到n只有200,大胆dp 用f[cnt][j]表示有cnt个数字,j个5,最多能拿到几个2。 输出max(min(i,f[k][i])) int n,k,f[210][4000],sum,ans; int main() { // freopen("1.in","r",stdin); n=read();k=read(); memset(f,0xef,sizeof(f)); f[0][0]=0; for(int i=1;i<=n;i++) { ll a=read(); int n2=0,n5=0; while(a%2==0) n2++,a/=2; while(a%5==0) n5++,a/=5; sum+=n5; for(int cnt=k;cnt>=1;cnt--) for(int j=sum;j>=n5;j--) f[cnt][j]=max(f[cnt][j],f[cnt-1][j-n5]+n2); } for(int i=0;i<=sum;i++) ans=max(ans,min(i,f[k][i])); cout<<ans; }
把描述st不相等的数组算一算 if(s[i]==t[i]) a[i]=a[i-1]; else if(s[i]=='1') a[i]=a[i-1]+1; else a[i]=a[i-1]-1; 则答案为a数组的max(a[l]-a[r]),维护一下前缀最大最小值并更新答案即可。 int n,a[1000010]; char s[1000010],t[1000010]; int main() { // freopen("1.in","r",stdin); n=read(); scanf("%s",s+1); scanf("%s",t+1); for(int i=1;i<=n;i++) { if(s[i]==t[i]) a[i]=a[i-1]; else if(s[i]=='1') a[i]=a[i-1]+1; else a[i]=a[i-1]-1; } if(a[n]) cout<<"-1"; else { int ans=0,minn=0,maxx=0; for(int i=1;i<=n;i++) { minn=min(minn,a[i]); maxx=max(maxx,a[i]); ans=max(ans,a[i]-minn); ans=max(ans,maxx-a[i]); } cout<<ans; } }