JOI 2019 Final(CSP_S 模拟赛)
T1 收集硬币(T4)
题意:有\(2n\)个点,坐标为\((x_{i},y_{i})\),每次只能选取一个硬币并将它移至相邻格子内。求将它们放在\(1\leq x\leq n,1\leq y\leq 2\)格子上且每个格子一个硬币的最小步数。移动中允许重叠。\(n\leq 10^5,-10^9\leq x_{i},y_{i}\leq 10^9\)
题解:显然首先这么放最优
设一个\(co[i][1/2]\)表示第\(i\)列第\(1/2\)行有多少个硬币(也就是右下角的那张表)
其次我们设两个变量\(d1,d2\)表示第\(1,2\)行可以往右推多少个。(负数就是往左推)
每到一列就\(d1+=co[i][1]-1\),然后如果上下一正一负则把绝对值小的变成\(0\)显然最优。
复杂度:\(O(n)\)
#include<bits/stdc++.h>
using namespace std;
int n,co[100005][3];
long long ans,d1,d2,t;
int main()
{
//freopen("coin.in","r",stdin),freopen("coin.out","w",stdout);
scanf("%d",&n);
int x,y;
for(int i=1;i<=2*n;++i)
{
scanf("%d%d",&x,&y);
if(x<1) ans+=1-x,x=1;
if(x>n) ans+=x-n,x=n;
if(y<1) ans+=1-y,y=1;
if(y>2) ans+=y-2,y=2;
++co[x][y];
}
for(int i=1;i<=n;++i)
{
d1+=co[i][1]-1;
d2+=co[i][2]-1;
if(d1>0&&d2<0)
{
t=min(d1,-d2);
ans+=t;zhanlanhui
d1-=t,d2+=t;
}
if(d2>0&&d1<0)
{
t=min(d2,-d1);
ans+=t;
d2-=t,d1+=t;
}
ans+=abs(d1);
ans+=abs(d2);
}
printf("%lld\n",ans);
return 0;
}
T2 展览会(T2)
题意:有\(n\)个画,每幅画有大小和美观值。有\(m\)个框,每个框有大小。只有画的大小不大于框的大小时画才能装进框里。求一个从左至右框大小依次增大且画的美观值依次增大的序列中最多能有多少幅画。\(n,m\leq10^5\),其他值\(\leq 10^9\)
题解:框从大到小排序,画按美观度从大到小排序,能放就放。
复杂度:\(O(n\log n)\)
#include<bits/stdc++.h>
using namespace std;
int n,co[100005][3];
long long ans,d1,d2,t;
int main()
{
//freopen("coin.in","r",stdin),freopen("coin.out","w",stdout);
scanf("%d",&n);
int x,y;
for(int i=1;i<=2*n;++i)
{
scanf("%d%d",&x,&y);
if(x<1) ans+=1-x,x=1;
if(x>n) ans+=x-n,x=n;
if(y<1) ans+=1-y,y=1;
if(y>2) ans+=y-2,y=2;
++co[x][y];
}
for(int i=1;i<=n;++i)
{
d1+=co[i][1]-1;
d2+=co[i][2]-1;
if(d1>0&&d2<0)
{
t=min(d1,-d2);
ans+=t;
d1-=t,d2+=t;
}
if(d2>0&&d1<0)
{
t=min(d2,-d1);
ans+=t;
d2-=t,d1+=t;
}
ans+=abs(d1);
ans+=abs(d2);
}
printf("%lld\n",ans);
return 0;
}
T3 独特的城市(T5)
题意:有一棵\(n\)个点的树,每个点有一种颜色。定义\(y\)被\(x\)支配当且仅当不存在\(z\)满足\(dis(x,z)=dis(x,y)\)。对于每个点,求出所有被他支配的点中有多少种不同的颜色。
\(n≤2*10^5\)
题解:这个讲的挺好
题解传送门
复杂度:\(O(n)\)
#include<bits/stdc++.h>
using namespace std;
#define maxn 200005
struct Edge
{
int fr,to;
}eg[maxn<<1];
int sta[maxn],buc[maxn],nowans,dist,s,t,top;
int edgenum,val[maxn],head[maxn];
int deep[maxn],len[maxn],ans[maxn],fson[maxn],sson[maxn];
inline void add(int fr,int to)
{
eg[++edgenum]=(Edge){head[fr],to};
head[fr]=edgenum;
}
void dfs_st(int rt,int fa,int dis)//求出直径s,t
{
if(dis>dist) dist=dis,s=rt;
for(int i=head[rt];i;i=eg[i].fr)
{
if(eg[i].to==fa) continue;
dfs_st(eg[i].to,rt,dis+1);
}
}
void dfs_pre(int rt,int fa)//长链剖分,求最长链和次长链对应的儿子
{
deep[rt]=deep[fa]+1;
len[rt]=1,fson[rt]=sson[rt]=0;
for(int i=head[rt];i;i=eg[i].fr)
{
if(eg[i].to==fa) continue;
dfs_pre(eg[i].to,rt);
if(len[eg[i].to]>len[fson[rt]])
sson[rt]=fson[rt],fson[rt]=eg[i].to,len[rt]=len[eg[i].to]+1;
else if(len[eg[i].to]>len[sson[rt]]) sson[rt]=eg[i].to;
}
}
inline void pluss(int x)//两个桶的基本操作
{
if(!buc[val[x]]++) ++nowans;
}
inline void minuss(int x)
{
if(!--buc[val[x]]) --nowans;
}
void solve(int rt,int fa)
{
while(top&&deep[rt]-deep[sta[top]]<=len[sson[rt]])
minuss(sta[top--]);
//先弹次长链长度都没有的
sta[++top]=rt;//压栈
//注意压栈是压进去有可能计入答案的点
pluss(rt);
if(fson[rt]) solve(fson[rt],rt);//重儿子
if(sta[top]==rt) minuss(sta[top--]);
while(top&&deep[rt]-deep[sta[top]]<=len[fson[rt]])
minuss(sta[top--]);
//把重儿子处理完了就直接弹掉没有最长链长度的
ans[rt]=max(ans[rt],nowans);
//以s和以t为根做两遍取max
for(int i=head[rt];i;i=eg[i].fr)
{
if(eg[i].to==fa||eg[i].to==fson[rt]) continue;
//每次递归轻儿子时都要压栈(因为对于轻儿子,这个点是有可能计入答案的)
if(sta[top]!=rt) sta[++top]=rt,pluss(rt);
solve(eg[i].to,rt);
}
if(sta[top]==rt) minuss(sta[top--]);//做完这个点就弹掉
}
int main()
{
//freopen("city.in","r",stdin),freopen("city.out","w",stdout);
int n,m,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
for(int i=1;i<=n;++i) scanf("%d",&val[i]);
dfs_st(1,0,0);
t=s,s=dist=0;
dfs_st(t,0,0);
dfs_pre(s,0),solve(s,0);
dfs_pre(t,0),solve(t,0);
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}