『模拟赛』暑假集训CSP提高模拟26
Rank
打得一般,倒数第二场了。。
A. 博弈
直接搬了牛客的一套题。
一眼没思路,模了一会放弃直接去打 T2 了,后来把 \(\mathcal{O(n^2)}\) 暴力 打了拿 30pts。
正解用到了异或哈希。首先确定合法的数量即为总对数 \(\frac{n(n-1)}{2}\) 减去不合法的数量,而比较显然的,不合法的判断条件为二者路径上每种长度边权均为偶数条,而相同的数异或和为 0,我们根据这个性质,算出每个点到跟的异或距离,并记录下每个距离的点数,每次增加点数前将答案减去当前距根等于该距离的点数,简单想想就能证出正确性。
当然,用哈希的原因是若边权很小,有可能出现异或后距离等于一个不相等的长度,如 \(3 \operatorname{xor} 5=6\),为尽可能避免这种情况,我们用随机数,将不会实际引用的边权重新赋值为一个更大的随机数,这样发生冲突的概率就很小了。
卡常的话就数组离散化,map 还是过于慢了。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{
char ch=getchar();lx x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=5e5+5;
const int inf=1e9;
int n,B;
int hh[N],to[N<<1],ne[N<<1],cnt;
ull w[N<<1],dis[N],ans;
mt19937_64 rng(random_device{}());
map<ull,ull>mp,g;
namespace Wisadel
{
void Wadd(int u,int v,ull va)
{
to[++cnt]=v;
w[cnt]=va;
ne[cnt]=hh[u];
hh[u]=cnt;
}
void Wdfs(int u,int fa,ull va)
{
dis[u]=dis[fa]^va;
ans-=g[dis[u]];
g[dis[u]]++;
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(v==fa) continue;
Wdfs(v,u,w[i]);
}
}
short main()
{
int T=qr;
while(T--)
{
n=qr;cnt=0;ans=1ll*n*(n-1)/2;
fill(hh+1,hh+1+n,-1);
g.clear();
fo(i,1,n-1)
{
ll a=qr,b=qr,c=qr;
if(!mp[c]) mp[c]=rng();
Wadd(a,b,mp[c]),Wadd(b,a,mp[c]);
}
Wdfs(1,0,0);
printf("%lld\n",ans);
}
return Ratio;
}
}
int main(){return Wisadel::main();}
B. 跳跃
shabi
一眼有思路,一个小时打出完整代码,自信到最后改个小错交了,结果半 RE 半 WA 保龄。
所以只能考虑正解,记录数对 \(f_i\) 包含从 \(1\) 跳到 \(i\) 的最小步数和该步数下能得到的最大分数,还要求出在点 \(i\) 向左跳能得到的最大分数,转移时枚举 \(i\) 之前的所有点,找到从该点跳到目标点的最小步数和得分,然后更新就行了,时间复杂度 \(\mathcal{O(n^2)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch=getchar();lx x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=1000+5;
const ll inf=1e18;
int n,k;
ll a[N],sum[N];
ll f[N],ans;
pair<ll,ll>ff[N];
namespace Wisadel
{
short main()
{
int TT=qr;
while(TT--)
{
n=qr,k=qr;ans=0;
fo(i,1,n) a[i]=qr,sum[i]=sum[i-1]+a[i];
ll zc=0;
fo(i,1,n)
{
ff[i]={inf,0};f[i]=0;
f[i]=sum[i]-zc;zc=min(zc,sum[i]);
if(sum[i]>=0) ff[i]={1,sum[i]};
}
ff[1]={0,0};
ans=max(ans,1ll*sum[1]*k);
fo(i,2,n)
{
fo(j,1,i-1)
{
ll minst=2+ff[j].fi,mamark=ff[j].se+2*f[j]+sum[i]-sum[j];
if(ff[j].fi>k||ff[j].se<0) continue;
if(mamark<0)
{
if(f[j]<=0) continue;
minst+=(-mamark+f[j]-1)/f[j];
mamark+=(-mamark+f[j]-1)/f[j]*f[j];
}
if(minst%2==0) minst++,mamark+=f[j];
if(minst<ff[i].fi) ff[i].fi=minst,ff[i].se=mamark;
else if(minst==ff[i].fi) ff[i].se=max(ff[i].se,mamark);
}
if(ff[i].fi<=k) ans=max(ans,ff[i].se+(k-ff[i].fi)*f[i]);
}
printf("%lld\n",ans);
}
return Ratio;
}
}
int main(){return Wisadel::main();}
C. 大陆
赛时想到了正解的做法,但嗓子肿了并有些困所以没精力实现,于是暴力特判 + 乱搞拿到 58pts。
先考虑若 \(B = 1\),把所有城市各自为省即可;若 \(3\times B\ge n\),将所有城市为一个省即可。
其他情况,递归至叶子结点向上操作,现将叶子结点们都置入同一个省,若数量 \(\ge B\) 则立即开新的省并将原来的省的省会置为该点;处理最后未成功分省的城市,直接将它们都放到上一个省内即可,因为我们的断省原则使得这样操作不会超过省最多城市的限制。整个过程只进行一遍 dfs,时间复杂度为 \(\mathcal{O(n)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch=getchar();lx x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=1e4+5;
const int inf=1e9;
int n,B;
int hh[N],to[N<<1],ne[N<<1],cnt;
int siz[N],bl[N],tot;
stack<int>st;
struct sheng
{
int sh,sum;
}sheng[N];
namespace Wisadel
{
void Wadd(int u,int v)
{
to[++cnt]=v;
ne[cnt]=hh[u];
hh[u]=cnt;
}
void Wdfs(int u,int fa)
{
int L=st.size();
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(v==fa) continue;
Wdfs(v,u);
if(st.size()-L>=B)
{
sheng[++tot].sh=u;
while(st.size()>L)
bl[st.top()]=tot,st.pop();
}
}
st.push(u);
}
short main()
{
// freopen(".in","r",stdin),freopen(".out","w",stdout);
n=qr,B=qr;
memset(hh,-1,sizeof hh);
fo(i,1,n-1)
{
int a=qr,b=qr;
Wadd(a,b),Wadd(b,a);
}
if(B==1)
{
printf("%d\n",n);
fo(i,1,n) printf("%d ",i);printf("\n");
fo(i,1,n) printf("%d ",i);printf("\n");
return Ratio;
}
if(3*B>=n)
{
printf("1\n");
fo(i,1,n) printf("1 ");printf("\n");
printf("1\n");
return Ratio;
}
Wdfs(1,0);
if(!tot) sheng[++tot].sh=1;
while(st.size()) bl[st.top()]=tot,st.pop();
printf("%d\n",tot);
fo(i,1,n) printf("%d ",bl[i]);printf("\n");
fo(i,1,tot) printf("%d ",sheng[i].sh);
return Ratio;
}
}
int main(){return Wisadel::main();}
D. 排列
一眼没啥思路遂直接暴力 40pts 后润。
正解是 FHQ-Treap,一眼看出码量,咕咕咕。
末
倒数第二场了,转眼暑假都过去了,真快,感觉还啥也没干啥也没玩啥也没学就结束了。
但其实往回看看,自己的成绩确实是在隐形中进步了,这就是学长说的打模拟赛能在无意识中提升实力吧。
明天最后一场了,(后天就放假了好耶,尽全力打吧,争取画上一个叹号或者圆满的句号吧。
完结撒花~