NOIP提高组模拟赛21
夜莺与玫瑰
想了大概\(2h\)也没搞出来,真的是。。。我太菜了
最后连\(60pts\)暴力都没调出来(式子没有\(max\)),还是时间分配不合理,留给打爆力的时间太少了。。。。。。。(最后\(10min\)能打出来才是奇迹好吧)
这题一看题面就想到仪仗队,然后就想用欧拉函数,就歪了
欧拉函数用不上,但是思想还是有借用的
用向量\((a,b)\)表示一个斜率的直线,借鉴思想容易发现当\(gcd(a,b)==1\)时才是需要统计的斜率
考虑有多少斜率为\((a,b)\)的直线(有多少\(a*b\)的矩形),显然有\((n-a)*(m-b)\)个,但是这样会重复计算,我们需要的是最左下方的一个,这样的矩形满足\(x<a||y<b\),(\((x,y)\)是矩形左下方点),或很恶心,正难则反,有\((n-a-a)*(m-b-b)\)个重复计算的,所以对于斜率\((a,b)\),有\((n-a)*(m-b)-(n-a-a)*(m-b-b)\)个矩形
吗?
\(n-a-a\)和\(m-b-b\)可能小于0,这个时候取0就可以了
(我是\(**\))
(下面的\(\sum\)的\(n,m\)好像需要\(-1\),懒得改了)
所以答案为
\(\displaystyle\sum_{a=1}^{n}\sum_{b=1}^{m}[gcd(a,b)==1]((n-a)*(m-b)-max(n-a-a,0)*max(m-b-b,0))\)
显然会\(TLE\),考虑化简这个式子,或者使用莫比乌斯反演(我太菜了不会,请找\(artalter\)大佬学习)
\(\displaystyle\sum_{i=a}^{n}\sum_{j=b}^{m}[gcd(a,b)==1](n-a)*(m-b)-\displaystyle\sum_{a=1}^{n}\sum_{b=1}^{m}[gcd(a,b)==1]max(n-a-a,0)*max(m-b-b,0)\)
\(max\)留着干啥?扔掉
\(\displaystyle\sum_{a=1}^{n}(n-a)\sum_{b=1}^{m}[gcd(a,b)==1](m-b)-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1](n-a*2)(m-b*2)\)
\(\displaystyle\sum_{a=1}^{n}(n-a)\sum_{b=1}^{m}[gcd(a,b)==1](m-b)-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}(n-a*2)\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1](m-b*2)\)
\(\displaystyle\sum_{a=1}^{n}(n-a)(m\sum_{b=1}^{m}[gcd(a,b)==1]-\sum_{b=1}^{m}[gcd(a,b)==1]b)-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}(n-a*2)(m\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1]-\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1](b*2))\)
\(O(n^2logn)\)预处理出来\(f[i][j]=\displaystyle\sum_{l=1}^{j}[gcd(i,l)==1]\)和\(g[i][j]=\displaystyle\sum_{l=1}^{j}[gcd(i,l)==1]l\)前缀和优化
原式转化\(\displaystyle\sum_{a=1}^{n}(n-a)(mf[a][m]-g[a][m])-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}(n-a*2)(mf[a][\lfloor \frac{m}{2}\rfloor]-2f[a][\lfloor \frac{m}{2}\rfloor])\)
复杂度\(O(n^2logn+Tn)\)
code
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=4005;
const int mod=1073741824;
int n,m;
int rem[maxn][maxn];
int ren[maxn][maxn];
int gcd(int x,int y){
if(y==0)return x;
return gcd(y,x%y);
}
void pre(){
for(int i=1;i<=4000;++i)
for(int j=1;j<=4000;++j){
rem[i][j]=rem[i][j-1],ren[i][j]=ren[i][j-1];
if(gcd(i,j)==1)++rem[i][j],ren[i][j]+=j;
}
}
void work(){
ll ans=0;
int ls=m-1;
for(int a=1;a<n;++a)
ans=(ans+(n-a)*(rem[a][ls]*m-ren[a][ls])%mod)%mod;
ls=n/2;int lr=m/2;
for(int a=1;a<=ls;++a)
ans=(ans-((n-a-a)*(rem[a][lr]*m-2*ren[a][lr])%mod)+mod)%mod;
printf("%lld\n",(ans*2+n+m)%mod);
}
int main(){
pre();
int T;scanf("%d",&T);
for(int ask=1;ask<=T;++ask){
scanf("%d%d",&n,&m);
work();
}
return 0;
}
B. 影子
距离正解那么近又那么远。
什么\(min\)太恶心了,对点权排序从大到小加点找直径避免掉
使用并查集维护某个子树中的直径及两个端点,每次加入新点将它与周围集合合并
设合并\(x,y\)两棵子树
新子树直径为\(x\)的直径、\(y\)的直径、\(x\)直径端点到\(y\)直径端点\(6\)种可能
用树剖或者倍增维护两点距离即可
code
#include <cstring>
#include <cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
typedef long long ll;
ll len[maxn][21];
struct node{int val,id;}d[maxn];
struct edge{int net,to,val;}e[maxn<<1|1];
struct SET{ll maxd;int l,r,fa;}f[maxn];
bool cmp(node x,node y){return x.val>y.val;}
int head[maxn],tot,n,fa[maxn][21],dep[maxn];
bool flag[maxn];
void add(int u,int v,int w){
e[++tot].net=head[u];head[u]=tot;
e[tot].to=v;e[tot].val=w;
}
void dfs(int x){
for(int i=head[x];i;i=e[i].net){
int v=e[i].to;
if(v==fa[x][0])continue;
fa[v][0]=x;dep[v]=dep[x]+1;
len[v][0]=e[i].val;
dfs(v);
}
}
int gf(int x){
return f[x].fa=f[x].fa==x?x:gf(f[x].fa);
}
ll dis(int u,int v){
if(dep[u]<dep[v])swap(u,v);
ll ans=0;
for(int i=20;i>=0;--i)if(dep[u]-dep[v]>=(1<<i))ans+=len[u][i],u=fa[u][i];
if(u==v)return ans;
for(int i=20;i>=0;--i)if(fa[u][i]!=fa[v][i])ans+=len[u][i]+len[v][i],u=fa[u][i],v=fa[v][i];
return ans+len[u][0]+len[v][0];
}
ll hb(int x,int y){
x=gf(x);y=gf(y);
if(x==y)return f[x].maxd;
int rl=0,rr=0;ll mx=-1,di;
di=dis(f[x].l,f[y].l);if(di>mx)mx=di,rl=f[x].l,rr=f[y].l;
di=dis(f[x].l,f[y].r);if(di>mx)mx=di,rl=f[x].l,rr=f[y].r;
di=dis(f[x].r,f[y].l);if(di>mx)mx=di,rl=f[x].r,rr=f[y].l;
di=dis(f[x].r,f[y].r);if(di>mx)mx=di,rl=f[x].r,rr=f[y].r;
f[y].fa=x;
if(f[x].maxd<f[y].maxd)f[x].maxd=f[y].maxd,f[x].l=f[y].l,f[x].r=f[y].r;
if(f[x].maxd<mx)f[x].maxd=mx,f[x].l=rl,f[x].r=rr;
return f[x].maxd;
}
ll work(int x){
ll ans=0;
for(int i=head[x];i;i=e[i].net){
int v=e[i].to;
if(!flag[v])continue;
ans=max(ans,hb(x,v));
}
return ans;
}
int main(){
// freopen("b.in","r",stdin);
int T;scanf("%d",&T);
for(int ask=1;ask<=T;++ask){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&d[i].val);
tot=0;
memset(fa,0,sizeof(fa));
memset(len,0,sizeof(len));
for(int i=1;i<=n;++i)head[i]=0;
for(int i=1;i<=n;++i)d[i].id=i;
for(int i=1;i<=n;++i)dep[i]=0;
for(int i=1;i<=n;++i)flag[i]=0;
for(int i=1;i<=n;++i)f[i].fa=f[i].l=f[i].r=i,f[i].maxd=0;
for(int i=1;i<n;++i){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs(1);
fa[1][0]=1;
for(int j=1;j<=20;++j)
for(int i=1;i<=n;++i){
fa[i][j]=fa[fa[i][j-1]][j-1];
len[i][j]=len[i][j-1]+len[fa[i][j-1]][j-1];
}
sort(d+1,d+n+1,cmp);
ll ans=0;
for(int i=1;i<=n;++i)flag[d[i].id]=1,ans=max(work(d[i].id)*d[i].val,ans);
printf("%lld\n",ans);
}
return 0;
}
C. 玫瑰花精
距离正解那么近又那么远。
线段树,类似山海经,左右边界特判!特判!
\(lxhcr\)大佬强烈推荐打平衡树
线段树+恶心码风
#include <cstring>
#include <algorithm>
#include <queue>
#include <cstdio>
using namespace std;
const int maxn=200005;
int rem[1000005];
struct node{
int prer,nxtl,mdl,mdr;
long long sum,pre,nxt,md;
};
int n,m;
struct tree{
node t[maxn<<2|1];
void push_up(int x){
int ls=x<<1,rs=x<<1|1;
t[x].sum=t[ls].sum+t[rs].sum;
if(t[ls].pre>=t[rs].pre+t[ls].sum){
t[x].pre=t[ls].pre;t[x].prer=t[ls].prer;
}else{
t[x].pre=t[ls].sum+t[rs].pre;t[x].prer=t[rs].prer;
}
if(t[ls].nxt+t[rs].sum>=t[rs].nxt){
t[x].nxt=t[ls].nxt+t[rs].sum;t[x].nxtl=t[ls].nxtl;
}else{
t[x].nxt=t[rs].nxt;t[x].nxtl=t[rs].nxtl;
}
t[x].md=t[ls].nxt+t[rs].pre;t[x].mdl=t[ls].nxtl;t[x].mdr=t[rs].prer;
if(((t[ls].md-1)>>1)>=((t[x].md-1)>>1)){
t[x].md=t[ls].md;t[x].mdl=t[ls].mdl;t[x].mdr=t[ls].mdr;
}
if(((t[rs].md-1)>>1)>((t[x].md-1)>>1)){
t[x].md=t[rs].md;t[x].mdl=t[rs].mdl;t[x].mdr=t[rs].mdr;
}
}
void built(int x,int l,int r){
if(l==r){
t[x].md=t[x].nxt=t[x].pre=t[x].sum=1;
t[x].mdr=t[x].nxtl=t[x].prer=t[x].mdl=l;
return;
}
int mid=(l+r)>>1;
built(x<<1,l,mid);
built(x<<1|1,mid+1,r);
push_up(x);
}
void modify(int x,int l,int r,int pos,int val){
if(l==r){
t[x].nxt=t[x].pre=t[x].md=t[x].sum=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)modify(x<<1,l,mid,pos,val);
else modify(x<<1|1,mid+1,r,pos,val);
push_up(x);
}
int pos(){
long long ans=0,mx=(t[1].md-1)/2;
ans=t[1].mdl+mx;
if(t[1].mdr==n)ans=n,mx=t[1].md;
if(t[1].mdl==1)ans=1,mx=t[1].md;
if(t[1].nxt-1>mx)ans=n,mx=t[1].nxt;
if (t[1].pre-1>=mx)ans=1,mx=t[1].pre;
return ans;
}
}T;
int main(){
scanf("%d%d",&n,&m);
T.built(1,1,n);
for(int i=1;i<=m;++i){
int op,x;
scanf("%d%d",&op,&x);
if(op&1){
int pos=T.pos();
printf("%d\n",pos);
rem[x]=pos;
T.modify(1,1,n,pos,-maxn);
}else{
T.modify(1,1,n,rem[x],1);
rem[x]=0;
}
}
return 0;
}