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. 大陆
原[SCOI2005] 王室联邦
赛时想到了正解的做法,但嗓子肿了并有些困所以没精力实现,于是暴力特判 + 乱搞拿到 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,一眼看出码量,咕咕咕。
末
倒数第二场了,转眼暑假都过去了,真快,感觉还啥也没干啥也没玩啥也没学就结束了。
但其实往回看看,自己的成绩确实是在隐形中进步了,这就是学长说的打模拟赛能在无意识中提升实力吧。
明天最后一场了,(后天就放假了好耶,尽全力打吧,争取画上一个叹号或者圆满的句号吧。
完结撒花~