CSP2024 前集训:多校A层冲刺NOIP2024模拟赛07
前言
T1 freopen
输入输出打反了!?!?!?!!然后我需要跑三遍生成树(两遍就行),别人只需要跑一遍感觉做法好劣啊。
T2 大模拟赛时没调出来。
T3 本来会曼哈顿距离与切比雪夫距离的相互转化,赛时竟然没有想到;因为常数巨大,OJ 上开了 \(5s\) 实现,accoders 甚至开了 \(7s\),结果双 \(\log\) 还是跑不过去,加了取模优化 accoders 过了,OJ 上只能 \(90pts\),正解单 \(\log\) 线段树合并都是擦边过,看在你部分分给得足的份上原谅你了。
T4 抽象玩意咕了咕了。
T1 限速
最优策略只可能出自于两种情况:
- 所选中所有权值 \(<k\),答案为 \(k-\) 所选出的最大权值。
- 所选中存在权值 \(\ge k\) 的权值,答案为这些权值 \(-k\) 的和。
两种情况分别跑一遍最小生成树即可。
对于第一种对于所有权值 \(<k\) 的边按权值从大到小选,此时可能无解,此时另一种情况一定有解。
对于第二种,在所有权值 \(>k\) 的中强制选择权值最小的一条边,再将所有边按照权值从小到大选。
两种答案取 \(\min\) 即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,k,tot,last,f[N]; ll ans,sum; bitset<N>vis;
struct aa {int x,y,w;}e[N];
int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
signed main()
{
freopen("speed.in","r",stdin),freopen("speed.out","w",stdout);
read(n,m,k); for(int i=1;i<=n;i++) f[i]=i;
for(int i=1,x,y,z;i<=m;i++) read(x,y,z),e[i]={x,y,z-k};
sort(e+1,e+1+m,[](aa a,aa b){return a.w<b.w;});
for(int i=1;i<=m;i++)
{
int x=find(e[i].x),y=find(e[i].y);
if(x==y) continue; f[x]=y,tot++,vis.set(i);
if(tot==n-1) {last=i; break;}
}
if(e[last].w>=0)
{for(int i=last;i>=1;i--) if(vis[i]&&e[i].w>=0) ans+=e[i].w;}
else
{
ans=-e[last].w; bool flag=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=last+1;i<=m;i++) if(e[i].w>=0)
{
if(e[i].w<ans)
f[find(e[i].x)]=find(e[i].y),tot=1,flag=1,sum=e[last=i].w;
else break;
}
if(flag)
{
if(tot==n-1) return write(sum),0;
for(int i=1;i<=m;i++)
{
int x=find(e[i].x),y=find(e[i].y);
if(x==y) continue; f[x]=y,tot++,sum+=max(0,e[i].w);
if(tot==n-1) {last=i; break;}
}
ans=min(ans,sum);
}
for(int i=1;i<=n;i++) f[i]=i;
if(!flag) last=m+1; sum=-e[last-1].w,tot=0;
for(int i=last-1;i;i--)
{
int x=find(e[i].x),y=find(e[i].y);
if(x==y) continue; f[x]=y,tot++;
if(tot==n-1) break;
}
if(tot==n-1) ans=min(ans,sum);
}
write(ans);
}
T2 酒鬼
大模拟不想写题解了啊啊啊,扔到 set 里,根据相邻的位移减时间差必须相同判无解。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define mkp make_pair
#define fi first
#define se second
using namespace std;
const int N=2e5+10,inf=1e9+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,mx=inf,tim=inf,last=-1; char op[4]; bool bad; set<pair<int,int> >s;
signed main()
{
freopen("drunkard.in","r",stdin),freopen("drunkard.out","w",stdout);
read(n,m); for(int x,y;m;m--)
{
scanf("%s",op);
if(bad) {if(op[1]=='l') read(x,y); else puts("bad");}
else if(op[1]=='l')
{
read(x,y); if(bad|=(x-1>y)) continue;
if(x>1) tim=min(tim,y),mx=min(mx,y-x+1);
auto it=s.insert(mkp(y,x)).fi,nxt=next(it);
if(it!=s.begin())
{
auto pre=prev(it);
if(bad|=abs(pre->se-x)>y-pre->fi) continue;
last=(nxt!=s.end()&&nxt->fi==last)?-1:last;
last=(y>last&&((pre->fi-pre->se)&1)!=((y-x)&1))?y:last;
}
if(nxt!=s.end())
{
if(bad|=abs(nxt->se-x)>nxt->fi-y) continue;
last=(nxt->fi>last&&((nxt->fi-nxt->se)&1)!=((y-x)&1))?nxt->fi:last;
}
bad|=(last>tim);
}
else if(op[1]=='i')
{
if(last==-1) write(s.size()?((s.begin()->fi-s.begin()->se+1)&1):0),puts("");
else
{
auto it=--s.lower_bound(mkp(last,0));
write(min(it->fi+1,last)),puts("");
}
}
else if(mx==inf) puts("inf"); else write(mx),puts("");
}
}
T3 距离
-
部分分 \(70pts\):对于每个点 \(O(n)\) 计算他的贡献,总复杂度 \(O(n^2)\)。
-
正解 \(80\sim 100pts\):
考虑切比雪夫距离转曼哈顿距离,同时 \(\min\) 转 \(\max\),有 \(a+b=\max(a,b)+\min(a,b)\),\((x_1,y_1),(x_2,y_2)\) 两点切比雪夫距离等于 \((\dfrac{x_1+y_1}{2},\dfrac{x_1-y_1}{2}),(\dfrac{x_2+y_2}{2},\dfrac{x_2-y_2}{2})\) 两点曼哈顿距离。
然后树上启发式合并套树状数组跑就行了,因为树状数组下标是权值所以要离散化,最后答案要 \(\times 2\),不妨把直接把 \(a_i,b_i\) 都 \(\times 2\),还省了求 \(2\) 的逆元了。
点击查看代码
#include<bits/stdc++.h> #define ll long long #define endl '\n' #define sort stable_sort #define pb push_back #define mkp make_pair #define fi first #define se second #define lowbit(x) (x&-x) using namespace std; const int N=5e5+10,P=1e9+7; #define LOCAL namespace IO{ #ifdef LOCAL FILE*Fin(fopen("distance.in","r")),*Fout(fopen("distance.out","w")); #else FILE*Fin(stdin),*Fout(stdout); #endif class qistream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qistream(FILE*_fp=stdin):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;}qistream&operator>>(char&x){x=getch();while(isspace(x))x=getch();return*this;}template<class T>qistream&operator>>(T&x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=x*10+buf[p]-'0';x=flag?-x:x;return*this;}char getch(){p+BLOCK>=SIZE?flush():void();return buf[p++];}qistream&operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i=0;for(;ch>' ';++i,ch=getch())str[i]=ch;str[i]='\0';return*this;}}qcin(Fin); class qostream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qostream(FILE*_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream&operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=-x,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while(x);for(int i=0,j=len-1;i<j;++i,--j)std::swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream&operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}qostream&operator<<(const char*s){for(int i=0;s[i];++i)putch(s[i]);return*this;}}qcout(Fout); } #define cin IO::qcin #define cout IO::qcout int n,tot,a[N],b[N],va[N],vb[N],sz[N],id[N],dfn[N],son[N],cnt[4],pos[4][N],bk[4][N]; ll ans[N]; vector<int>e[N]; inline int mod(int x,int y) {x+=y; return x>=P?x-P:x;} struct aa { int c1[N],c2[N]; void add(int x,int d) {for(;x<=n;x+=lowbit(x)) c1[x]=mod(c1[x],d),c2[x]++;} void del(int x,int d) {for(;x<=n;x+=lowbit(x)) c1[x]=mod(c1[x],P-d),c2[x]--;} pair<ll,int> ask(int x) { pair<ll,int>res=mkp(0,0); for(;x;x-=lowbit(x)) res.fi=mod(res.fi,c1[x]),res.se+=c2[x]; return res; } pair<ll,int> ask(int l,int r) {auto x=ask(l-1),y=ask(r); return mkp(mod(y.fi,P-x.fi),y.se-x.se);} }t[4]; inline void dfs1(int x,int fa) { sz[id[dfn[x]=++tot]=x]=1; for(int y:e[x]) if(y!=fa) dfs1(y,x),sz[x]+=sz[y],son[x]=sz[y]>sz[son[x]]?y:son[x]; } inline void calc(int x,int y) { auto tmp=t[0].ask(a[y]-1); ans[x]=mod(ans[x],mod(1ll*pos[0][a[y]]*tmp.se%P,P-tmp.fi)); tmp=t[0].ask(a[y]+1,cnt[0]); ans[x]=mod(ans[x],mod(tmp.fi,P-1ll*pos[0][a[y]]*tmp.se%P)); tmp=t[1].ask(b[y]-1); ans[x]=mod(ans[x],mod(1ll*pos[1][b[y]]*tmp.se%P,P-tmp.fi)); tmp=t[1].ask(b[y]+1,cnt[1]); ans[x]=mod(ans[x],mod(tmp.fi,P-1ll*pos[1][b[y]]*tmp.se%P)); tmp=t[2].ask(va[y]-1); ans[x]=mod(ans[x],P-mod(1ll*pos[2][va[y]]*tmp.se%P,P-tmp.fi)); tmp=t[2].ask(va[y]+1,cnt[2]); ans[x]=mod(ans[x],P-mod(tmp.fi,P-1ll*pos[2][va[y]]*tmp.se%P)); tmp=t[3].ask(vb[y]-1); ans[x]=mod(ans[x],P-mod(1ll*pos[3][vb[y]]*tmp.se%P,P-tmp.fi)); tmp=t[3].ask(vb[y]+1,cnt[3]); ans[x]=mod(ans[x],P-mod(tmp.fi,P-1ll*pos[3][vb[y]]*tmp.se%P)); } inline void insert(int y) { t[0].add(a[y],pos[0][a[y]]),t[1].add(b[y],pos[1][b[y]]); t[2].add(va[y],pos[2][va[y]]),t[3].add(vb[y],pos[3][vb[y]]); } inline void erase(int y) { t[0].del(a[y],pos[0][a[y]]),t[1].del(b[y],pos[1][b[y]]); t[2].del(va[y],pos[2][va[y]]),t[3].del(vb[y],pos[3][vb[y]]); } inline void dfs2(int x,int fa,bool flag) { for(int y:e[x]) if(y!=fa&&y!=son[x]) dfs2(y,x,0),ans[x]=mod(ans[x],ans[y]); if(son[x]) dfs2(son[x],x,1),ans[x]=mod(ans[x],ans[son[x]]); for(int y:e[x]) if(y!=fa&&y!=son[x]) { for(int i=dfn[y],z;i<=dfn[y]+sz[y]-1;i++) calc(x,id[i]); for(int i=dfn[y],z;i<=dfn[y]+sz[y]-1;i++) insert(id[i]); } calc(x,x),insert(x); if(!flag) for(int i=dfn[x],z;i<=dfn[x]+sz[x]-1;i++) erase(id[i]); } signed main() { cin>>n; for(int i=1,x,y;i<n;i++) cin>>x>>y,e[x].pb(y),e[y].pb(x); dfs1(1,0); for(int i=1;i<=n;i++) { cin>>a[i]>>b[i],bk[2][i]=va[i]=a[i]+b[i],bk[3][i]=vb[i]=a[i]-b[i]; bk[0][i]=(a[i]<<=1),bk[1][i]=(b[i]<<=1); } sort(bk[0]+1,bk[0]+1+n),cnt[0]=unique(bk[0]+1,bk[0]+1+n)-bk[0]-1; sort(bk[1]+1,bk[1]+1+n),cnt[1]=unique(bk[1]+1,bk[1]+1+n)-bk[1]-1; sort(bk[2]+1,bk[2]+1+n),cnt[2]=unique(bk[2]+1,bk[2]+1+n)-bk[2]-1; sort(bk[3]+1,bk[3]+1+n),cnt[3]=unique(bk[3]+1,bk[3]+1+n)-bk[3]-1; for(int i=1,tmp[4];i<=n;i++) { tmp[0]=lower_bound(bk[0]+1,bk[0]+1+cnt[0],a[i])-bk[0]; tmp[1]=lower_bound(bk[1]+1,bk[1]+1+cnt[1],b[i])-bk[1]; tmp[2]=lower_bound(bk[2]+1,bk[2]+1+cnt[2],va[i])-bk[2]; tmp[3]=lower_bound(bk[3]+1,bk[3]+1+cnt[3],vb[i])-bk[3]; pos[0][tmp[0]]=a[i]%P,a[i]=tmp[0]; pos[1][tmp[1]]=b[i]%P,b[i]=tmp[1]; pos[2][tmp[2]]=va[i]%P,va[i]=tmp[2]; pos[3][tmp[3]]=mod(vb[i],P),vb[i]=tmp[3]; } dfs2(1,0,1); for(int i=1;i<=n;i++) cout<<ans[i]<<endl; }
-
正解 \(100pts\):
一样的思路但是改成线段树合并,每个点针对其子树开值域线段树,定义 \(val_{l,r},sum_{l,r},cnt_{l,r}\) 分别表示 \(a_{[l,r]}\) 贡献、元素和、元素数量,有 pushup :\(val_{l,r}=ans_{l,mid}+ans_{mid+1,r}+sum_{l,r}\times cnt_{mid+1,r}+sum_{mid+1,r}\times cnt_{l,mid}\)。
拆成四部分分别跑一遍即可。
懒得打了,甚至比上一个做法好打得多。
T4 团队选拔
抽象玩意,只会 \(16pts\) 部分分,咕了咕了。