呜呜呜,这场爆0了,过于渴望T1正解导致暴力。。。
T1
一个数学题,用n^4的方法枚举两个点然后去重,可以拿到40分
我们再看n^2的做法,首先,平行于坐标轴的直线有n+m条,任意一条斜率为正且不垂直于坐标轴都可以把斜率取反,对应另一条直线
所以我们只考虑斜率为正且不垂直于坐标轴的直线。
考虑枚举方向向量a,b显然有gcd(a,b)=1,我们定义一个点(x,y)的前驱后继分别为(x-a,y-b),(x+a,y+b),
则直线的数量满足它的前驱和自己在这个矩阵内,但他的后继不在的点的数量
然后我们经过大力计算发现式子为
(n-a)(m-b)-max(n-2a,0)(m-2b,0);
那么ans就等于
sigma(i=1,i<n)sigma(j=1,j<m)(gcd( i , j )==1)*(n-a)(m-b)-max(n-2a,0)(m-2b,0);
由于官方给的二维前缀和我不太懂,所以这里给出另一种解法
我们发现只有在gcd(i,j)=1的情况会作出贡献,那么我们定义两个数组
num[ i ][ j ]表示从1到j与i互质的数的个数,sum[ i ][ j ]表示从1到j与i互质的数的和
那么ans=sigma(i=1,i<n)(num[ i ][ m-1 ]*m-sum[ i ][ m-1 ])*( n-i )-( 2*i<n )( n-2*i )*(num[ i ][ m/2 ]*m-2*sum[ i ][ m/2 ])
处理的时候可以数组模拟辗转相除,把预处理的n^2log(n)降低成n^2
那么总时间复杂度就是n^2+n*T;
#include<bits/stdc++.h> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline int min(int x,int y){return x<y?x:y;} inline int max(int x,int y){return x>y?x:y;} inline void swap(int &x,int &y){x^=y^=x^=y;} inline int abs(int x){return x<0?-x:x;} inline int gcd(int a,int b){return b?gcd(b,a%b):a;} inline int lcm(int x,int y){return x/gcd(x,y)*y;} const int maxn=4001; const int mod=1<<30; int ans=0; int sum[maxn+1][maxn+1]; short ggcd[maxn+1][maxn+1],g[maxn+1][maxn+1]; signed main() { //freopen("out","w",stdout); for(int i=1;i<=maxn;i++) { g[i][i]=g[i][0]=g[0][i]=i; for(int j=1;j<i;j++) g[i][j]=g[j][i]=g[j][i%j]; } for(int i=1;i<=maxn;i++) { for(int j=1;j<=maxn;j++) { ggcd[i][j]=ggcd[i][j-1]; sum[i][j]=sum[i][j-1]; if(g[i][j]==1) ggcd[i][j]++,sum[i][j]+=j; } } int t=read(); while(t--) { long long ans=0; int n=read(); int m=read(); for(int i=1;i<n;i++) { (ans+=((long long)ggcd[i][m-1]*m%mod-sum[i][m-1]+mod)*(n-i))%=mod; if(2*i<n) ((ans-=(long long)(n-i*2)*(ggcd[i][m/2]*m%mod-2*sum[i][m/2]%mod))+=mod)%=mod; } printf("%lld\n",(ans*2+n+m+mod)%mod); } }
T2影子
这题树剖暴力有35,犯了个智障错误爆0了;
说下正解:
将所有点按照权值从大到小排序,对于将当前点和与其相连的所有点依次合
并到一个集合中。并查集需要维护当前集合中的最长路径长度和对应的两个端点。
在合并两个集合后,最终集合的最长路一定只有两类情况:一类是其中一个
集合的最长路,一共有 2 种;一类是由两个集合的最长路的端点互相连接而成,
一共有 2×2=4 种。需要用到最近公共祖先的算法预处理求两点在树上的距离,
离线处理即可。每次合并并查集之后用当前点的权值乘以最长路的总长度来更新
最优结果即可。即使这个点不在当前合并后的集合的最长路上也是没有问题的,
因为如果这样的话,必然已经在之前得到了对应的结果,这次合并不会对最终结
果产生影响。
复杂度。。不会证明
#include<bits/stdc++.h> #define jb cout<<"jb"; #define int long long using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int maxn=1e5+1; int cnt,num,n; int w[maxn],dis[maxn],head[maxn],to[maxn*2],val[maxn*2]; int nxt[maxn*2],size[maxn],top[maxn],fa[maxn],son[maxn],dfn[maxn]; int ans; int dep[maxn]; struct node{ int id,val; }a[maxn]; struct tree{ int fa,l,r,len; }f[maxn],ff; inline int min(int x,int y){return x<y?x:y;} inline int max(int x,int y){return x>y?x:y;} inline void swap(int &x,int &y){x^=y^=x^=y;} inline int abs(int x){return x<0?-x:x;} inline int gcd(int x,int y){return y?x:gcd(y,x%y);} inline int lcm(int x,int y){return x/gcd(x,y)*y;} inline void clear(){memset(son,0,sizeof(son));memset(head,0,sizeof(head));for(int i=1;i<=n;i++)f[i]=ff; cnt=0,num=0,ans=0;} inline void add(int x,int y,int z){to[++num]=y;nxt[num]=head[x]; val[num]=z; head[x]=num;} inline bool cmp(node a,node b){return a.val>b.val;} inline int getfa(int x) { if(f[x].fa!=x) f[x].fa=getfa(f[x].fa); return f[x].fa; } void dfs1(int x,int ff) { fa[x]=ff; size[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==ff) continue; fa[y]=x; dep[y]=dep[x]+1; dis[y]=dis[x]+val[i]; dfs1(y,x); size[x]+=size[y]; if(size[son[x]]<size[y]) son[x]=y; } } void dfs2(int x,int ff) { top[x]=ff; if(!son[x]) return ; dfs2(son[x],ff); for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==son[x]||y==fa[x]) continue; dfs2(y,y); } } inline int lca(int x, int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } return dep[x]<dep[y]?x:y; } void merge(int x,int y,int val,int now) { int maxx=0; x=getfa(f[x].fa),y=getfa(f[y].fa); f[y].fa=x; int xl=f[x].l,xr=f[x].r,yl=f[y].l,yr=f[y].r; int dis1=f[x].len,dis2=f[y].len; int dis3=dis[xl]+dis[yl]-2*dis[lca(xl,yl)]; int dis4=dis[xl]+dis[yr]-2*dis[lca(xl,yr)]; int dis5=dis[xr]+dis[yl]-2*dis[lca(xr,yl)]; int dis6=dis[xr]+dis[yr]-2*dis[lca(xr,yr)]; if(dis2>dis1) { f[x].l=f[y].l,f[x].r=f[y].r; f[x].len=dis2; } if(dis3>f[x].len) { f[x].l=xl,f[x].r=yl; f[x].len=dis3; } if(dis4>f[x].len) { f[x].l=xl,f[x].r=yr; f[x].len=dis4; } if(dis5>f[x].len) { f[x].l=xr,f[x].r=yl; f[x].len=dis5; } if(dis6>f[x].len) { f[x].l=xr,f[x].r=yr; f[x].len=dis6; } ans=max(ans,f[x].len*w[now]); } signed main() { int t=read(); while(t--) { clear(); n=read(); for(int i=1;i<=n;i++) { w[i]=read(); a[i].val=w[i],a[i].id=i; f[i].fa=f[i].l=f[i].r=i; } int x,y,z; for(int i=1;i<=n-1;i++) { x=read(),y=read(),z=read(); add(x,y,z); add(y,x,z); } dfs1(1,0); dfs2(1,1); sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) { int x=a[i].id; for(int j=head[x];j;j=nxt[j]) { int y=to[j]; if(w[y]>=a[i].val) { merge(x,y,val[j],x); } } } cout<<ans<<endl; } }
T3玫瑰花精
开始想打一棵平衡树,结果给打假了,后来发现其实跟之前考过的一道hotel差不多
用普通线段树维护一下l到r最左边的那个花精,最右边的,以及中间最大的连续无花精的区间,以及他们的中点p
然后就是一下基础的操作了,pushup稍微注意一下就好了
#include<bits/stdc++.h> #define lid id<<1 #define rid id<<1|1 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int maxn=2000001; int in[maxn],n,m; struct tree{ int ls,rs,mid,p,l,r; }tr[maxn<<2]; void pushup(int id) { if(tr[lid].ls) tr[id].ls=tr[lid].ls; else tr[id].ls=tr[rid].ls; if(tr[rid].rs) tr[id].rs=tr[rid].rs; else tr[id].rs=tr[lid].rs; if(!tr[rid].ls){tr[id].mid=tr[lid].mid;tr[id].p=tr[lid].p;return ;} if(!tr[lid].ls){tr[id].mid=tr[rid].mid;tr[id].p=tr[rid].p;return ;} int tmp=(tr[rid].ls-tr[lid].rs)>>1; if(tr[lid].mid>=tmp&&tr[lid].mid>=tr[rid].mid) tr[id].mid=tr[lid].mid,tr[id].p=tr[lid].p; else if(tmp>=tr[rid].mid) tr[id].mid=tmp,tr[id].p=(tr[rid].ls+tr[lid].rs)>>1; else tr[id].mid=tr[rid].mid,tr[id].p=tr[rid].p; } inline void build(int id,int l,int r) { tr[id].l=l; tr[id].r=r; if(l==r) return ; int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); } inline void update(int id,int pos,int val) { if(tr[id].l==tr[id].r) { if(val==1) { tr[id].ls=tr[id].l; tr[id].rs=tr[id].r; tr[id].p=0; tr[id].mid=0; return ; } if(val==-1) { tr[id].p=0; tr[id].ls=0; tr[id].rs=0; tr[id].mid=0; return ; } } int mid=(tr[id].l+tr[id].r)>>1; if(pos<=mid) update(lid,pos,val); else update(rid,pos,val); pushup(id); } int main() { //freopen("a.in","r",stdin); //freopen("out","w",stdout); n=read(); m=read(); int pot,a; build(1,1,n); for(int i=1;i<=m;i++) { pot=read(),a=read(); if(pot==1) { if(tr[1].ls==0) { in[a]=1; printf("%d\n",in[a]); update(1,in[a],1); continue; } int minn=-0x7fffffff; if(tr[1].ls-1>minn) { minn=tr[1].ls-1; in[a]=1; } if(tr[1].mid>minn) { minn=tr[1].mid; in[a]=tr[1].p; } if(n-tr[1].rs>minn) { in[a]=n; } printf("%d\n",in[a]); update(1,in[a],1); } else { update(1,in[a],-1); } } }