NOIP提高組模擬賽21
夜鶯與玫瑰
想了大概\(2h\)也沒搞出來,真的是。。。我太菜了
最後連\(60pts\)暴力都沒調出來(式子沒有\(max\)),還是時間分配不合理,留給打爆力的時間太少了。。。。。。。(最後\(10min\)能打出來才是奇蹟好吧)
這題一看題面就想到儀仗隊,然後就想用尤拉函式,就歪了
尤拉函式用不上,但是思想還是有借用的
用向量\((a,b)\)表示一個斜率的直線,借鑑思想容易發現當\(gcd(a,b)==1\)時才是需要統計的斜率
考慮有多少斜率為\((a,b)\)的直線(有多少\(a*b\)的矩形),顯然有\((n-a)*(m-b)\)個,但是這樣會重複計算,我們需要的是最左下方的一個,這樣的矩形滿足\(x<a||y<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\)
\(\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;
}