Preface
答辩圈钱杯,去年省赛的题好歹还有些有意思的题(指杜教筛),今年变成煞笔题开会了是吧
两个小时多一点就全写完了,然后开始给后面三题写对拍(结果发现其实都没写挂纯在浪费时间)
考完感觉AK有望,结果后面发现最后一题漏看条件了,苦露西
而且中间EF两题全偷懒开__int128
了,反正用下发的编译器开C++11是能跑的,也看的网上有其他人用了__int128
,希望别CE的说
试题 A: 艺术与篮球
签到,直接枚举判断就好了,话说这种日期处理的相关题是蓝桥杯特色环节吗
答案应该是3228
,但两个填空题比赛时候的代码不知道为什么被我弄没了,没法贴在这里的说
试题 B: 五子棋对弈
还是大力枚举题,但要注意白子/黑子的数量应该是\(13/12\),看到好多错的就是忘记判这个了
答案应该是3126376
试题 C: 训练士兵
刚开始以为答案关于组团训练的次数有三分性之类的,后面看了眼数据范围原来可以直接枚举组团训练的次数\(t\)
然后需要维护的就是\(c_i>t\)的士兵的\(\sum c_i\times p_i\)和\(\sum p_i\)的值了,拿个后缀和随便维护一下
#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5;
int n,S,p,c,mx_c,spx[N],sfx[N];
signed main()
{
RI i; for (scanf("%lld%lld",&n,&S),i=1;i<=n;++i)
scanf("%lld%lld",&p,&c),mx_c=max(mx_c,c),spx[c]+=p,sfx[c]+=c*p;
for (i=mx_c-1;i>=1;--i) spx[i]+=spx[i+1],sfx[i]+=sfx[i+1];
int ans=sfx[1]; for (i=1;i<=mx_c;++i)
ans=min(ans,i*S+(sfx[i]-spx[i]*i));
return printf("%lld",ans),0;
}
试题 D: 团建
据说是个傻逼题,但有铸币做不来怎么办
没关系直接大力Hash就完事,对两棵树的每个根到当前点的路径做Hash,然后拿个map
维护下匹配即可
写了双Hash应该也不会挂,自从用了这份模数后好像都没被卡过Hash
#include<cstdio>
#include<iostream>
#include<vector>
#include<set>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,mod1=998244353,mod2=1e9+7;
int n,m,x,y,a[N],b[N],ans; vector <int> A[N],B[N];
struct Hasher
{
int x,y;
inline Hasher(CI X=0,CI Y=0)
{
x=X; y=Y;
}
friend inline bool operator < (const Hasher& A,const Hasher& B)
{
return A.x!=B.x?A.x<B.x:A.y<B.y;
}
friend inline Hasher operator + (const Hasher& A,const Hasher& B)
{
return Hasher((A.x+B.x)%mod1,(A.y+B.y)%mod2);
}
friend inline Hasher operator - (const Hasher& A,const Hasher& B)
{
return Hasher((A.x-B.x+mod1)%mod1,(A.y-B.y+mod2)%mod2);
}
friend inline Hasher operator * (const Hasher& A,const Hasher& B)
{
return Hasher(1LL*A.x*B.x%mod1,1LL*A.y*B.y%mod2);
}
}; set <Hasher> rst;
const Hasher seed=Hasher(31,131);
inline void DFS1(CI now=1,CI fa=0,Hasher pre=Hasher())
{
pre=pre*seed+Hasher(a[now],a[now]); rst.insert(pre);
for (auto to:A[now]) if (to!=fa) DFS1(to,now,pre);
}
inline void DFS2(CI now=1,CI fa=0,CI dep=1,Hasher pre=Hasher())
{
pre=pre*seed+Hasher(b[now],b[now]);
if (rst.count(pre)) ans=max(ans,dep);
for (auto to:B[now]) if (to!=fa) DFS2(to,now,dep+1,pre);
}
int main()
{
RI i; scanf("%d%d",&n,&m);
for (i=1;i<=n;++i) scanf("%d",&a[i]);
for (i=1;i<=m;++i) scanf("%d",&b[i]);
for (i=1;i<n;++i) scanf("%d%d",&x,&y),A[x].push_back(y),A[y].push_back(x);
for (i=1;i<m;++i) scanf("%d%d",&x,&y),B[x].push_back(y),B[y].push_back(x);
return DFS1(),DFS2(),printf("%d",ans),0;
}
试题 E: 成绩统计
首先一眼二分答案\(x\),考虑如何check(x)
不难发现对于\(1\sim x\)这段前缀,先将所有数排个序,然后每次选相邻的\(k\)个一定是最优的
但计算方差的时候显然直接按照定义来不好处理,我们用\(D(x)=E(X^2)-[E(x)]^2\)转化一下就很容易用前缀和处理了
但要注意把比较的时候分母全乘到分子上时会爆long long
,需要开__int128
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,k,T,a[N],b[N]; long long pfx[N],pfx2[N];
inline bool check(CI x)
{
RI i; for (i=1;i<=x;++i) b[i]=a[i]; sort(b+1,b+x+1);
for (i=1;i<=x;++i) pfx[i]=pfx[i-1]+b[i],pfx2[i]=pfx2[i-1]+1LL*b[i]*b[i];
for (i=k;i<=x;++i)
{
long long sum=pfx[i]-pfx[i-k],sum2=pfx2[i]-pfx2[i-k];
if ((__int128)(sum2)*k-(__int128)(sum)*sum<(__int128)(T)*k*k) return 1;
}
return 0;
}
int main()
{
RI i; for (scanf("%d%d%d",&n,&k,&T),i=1;i<=n;++i) scanf("%d",&a[i]);
int l=k,r=n,mid,ret=-1; while (l<=r)
if (check(mid=l+r>>1)) ret=mid,r=mid-1; else l=mid+1;
return printf("%d",ret),0;
}
试题 F: 因数计数
感觉算是本场最难的一个题了,我当时是看一眼觉得有点烦然后直接跳了,写完后面两个一眼题后回头写的
刚开始的做法是考虑确定四元组\((i,j,k,l)\)中的前两项再计数,但感觉要讨论很多并且复杂度也不好优化
后面考虑先枚举确定\(i,k\),因为它们有倍数关系所以直接枚举复杂度是\(O(n\log n)\)的(假设\(n\)和值域同阶)
然后我们要考虑的就是当少了确定的这两个数后,剩下的有倍数关系的数对的数量
这个很容易想到容斥计算,用初始时全局的倍数关系数对的数量减去\(i,k\)各自带来的影响即可
注意需要分\(i\)是否等于\(k\)两种情况来讨论计算,代码还是很好写的
(PS:最后如果所有数都相同答案就是\(A_n^4\),会爆long long
,因此又要开__int128
)
#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,m,x,c[N],f[N],all; __int128 ans;
inline void write(__int128 x)
{
if (x==0) return (void)(putchar('0'));
if (x>9) write(x/10); putchar(x%10+'0');
}
signed main()
{
//freopen("F.in","r",stdin); freopen("F.out","w",stdout);
RI i,j; for (scanf("%lld",&n),i=1;i<=n;++i)
scanf("%lld",&x),++c[x],m=max(m,x);
for (i=1;i<=m;++i) if (c[i])
for (j=i*2;j<=m;j+=i) if (c[j])
f[i]+=c[j],f[j]+=c[i],all+=c[i]*c[j];
for (i=1;i<=m;++i) all+=c[i]*(c[i]-1);
for (i=1;i<=m;++i) if (c[i])
{
auto delta=[&](CI x,CI d)
{
return x*(x-1)-(x-d)*(x-d-1);
};
if (c[i]>=2) ans+=(__int128)(c[i])*(c[i]-1)*(all-delta(c[i],2)-f[i]*2);
for (j=i*2;j<=m;j+=i) if (c[j]) ans+=(__int128)(c[i])*c[j]*(all-delta(c[i],1)-delta(c[j],1)-f[i]-f[j]+1);
}
return write(ans),0;
}
试题 G: 零食采购
这TM绝对是很久之前的NOIP原题了,我感觉我以前肯定做过,而且颜色数\(20\)也是很经典了
刚开始看完题以为要写树上莫队了,感觉这东西讨论起来有点小复杂,很久没写都快忘了
后面一看数据范围\(c_i\le 20\)直接乐了,直接把每种颜色状压然后合并的时候或一下即可
本来以为要写树剖的以为又要被板子题腐乳了,后面一想妈的又没有修改直接写个倍增完事
#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,q,c[N],x,y,cnt[(1<<20)+5]; vector <int> v[N];
int anc[N][20],col[N][20],dep[N];
inline void DFS(CI now=1,CI fa=0)
{
dep[now]=dep[fa]+1; anc[now][0]=fa; col[now][0]=(1<<c[now]);
for (RI i=0;i<19;++i) if (anc[now][i])
anc[now][i+1]=anc[anc[now][i]][i],col[now][i+1]=col[now][i]|col[anc[now][i]][i]; else break;
for (auto to:v[now]) if (to!=fa) DFS(to,now);
}
inline int getLCA(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y);
for (i=19;i>=0;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i];
if (x==y) return x;
for (i=19;i>=0;--i) if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
return anc[x][0];
}
inline int getcol(int x,CI y)
{
int ret=0; for (RI i=19;i>=0;--i)
if (dep[anc[x][i]]>=dep[y]) ret|=col[x][i],x=anc[x][i];
return ret|(1<<c[y]);
}
int main()
{
//freopen("G.in","r",stdin); freopen("G.out","w",stdout);
RI i; for (scanf("%d%d",&n,&q),i=1;i<=n;++i) scanf("%d",&c[i]),--c[i];
for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
for (i=1;i<(1<<20);++i) cnt[i]=cnt[i>>1]+(i&1);
for (DFS(),i=1;i<=q;++i)
{
scanf("%d%d",&x,&y); int z=getLCA(x,y);
printf("%d\n",cnt[getcol(x,z)|getcol(y,z)]);
}
return 0;
}
试题 H: 封印宝石
这种字典序最大/最小的题目都是一套流程,按位枚举然后贪心选,这题也不例外
从左往右枚举当前位置\(i\),考虑能放入当前盒子的宝石范围为\([i,i+k]\),那么显然从里面挑一个最大的即可
如果有多个最大的那么显然先拿靠左的一定更优,然后随便写个线段树维护下就行了
结果赛后才发现有个“相邻的两个盒子的宝石不能相同”,那就再额外维护个严格次大值以及其最左的位置即可
(PS:本题代码在赛后改过,加上了找次大值的部分,但很奇怪在民间数据的dashOJ上还是无法通过,合理推测是造的数据锅了,因为除了admin好像没一个过的)
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,k,a[N],ans[N];
struct ifo
{
int mx,mxp,smx,smxp;
inline ifo(CI MX=-1e9,CI MXP=0,CI SMX=-2e9,CI SMXP=0)
{
mx=MX; mxp=MXP; smx=SMX; smxp=SMXP;
}
};
class Segment_Tree
{
private:
ifo O[N<<2];
inline ifo merge(const ifo& A,const ifo& B)
{
ifo C; if (A.mx>B.mx)
{
C.mx=A.mx; C.mxp=A.mxp;
if (A.smx>=B.mx) C.smx=A.smx,C.smxp=A.smxp;
else C.smx=B.mx,C.smxp=B.mxp;
} else if (A.mx<B.mx)
{
C.mx=B.mx; C.mxp=B.mxp;
if (A.mx>=B.smx) C.smx=A.mx,C.smxp=A.mxp;
else C.smx=B.smx,C.smxp=B.smxp;
} else
{
C.mx=A.mx; C.mxp=A.mxp;
if (A.smx>=B.smx) C.smx=A.smx,C.smxp=A.smxp;
else C.smx=B.smx,C.smxp=B.smxp;
}
return C;
}
public:
#define TN CI now=1,CI l=1,CI r=n
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
inline void build(TN)
{
if (l==r) return (void)(O[now]=ifo(a[l],l));
int mid=l+r>>1; build(LS); build(RS);
O[now]=merge(O[now<<1],O[now<<1|1]);
}
inline ifo query(CI beg,CI end,TN)
{
if (beg<=l&&r<=end) return O[now]; int mid=l+r>>1; ifo ret;
if (beg<=mid) ret=merge(ret,query(beg,end,LS));
if (end>mid) ret=merge(ret,query(beg,end,RS)); return ret;
}
inline void updata(CI pos,TN)
{
if (l==r) return (void)(O[now]=ifo(-1,l)); int mid=l+r>>1;
if (pos<=mid) updata(pos,LS); else updata(pos,RS);
O[now]=merge(O[now<<1],O[now<<1|1]);
}
#undef TN
#undef LS
#undef RS
}SEG;
int main()
{
//freopen("H.in","r",stdin); freopen("H.out","w",stdout);
RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]);
for (SEG.build(),ans[0]=-1,i=1;i<=n;++i)
{
int pos=SEG.query(i,min(n,i+k)).mxp;
if (a[pos]==-1) { ans[i]=-1; continue; }
if (ans[i-1]==-1||a[pos]!=ans[i-1])
{
ans[i]=a[pos]; a[pos]=-1;
k-=(pos-i); SEG.updata(pos);
} else
{
pos=SEG.query(i,min(n,i+k)).smxp;
if (a[pos]==-1) { ans[i]=-1; continue; }
ans[i]=a[pos]; a[pos]=-1;
k-=(pos-i); SEG.updata(pos);
}
}
for (i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
return 0;
}
Postscript
感觉今年只要EF不要因为__int128
CE还是随便拿省一的说
但不管怎么说既然来打圈钱杯了目标肯定就是国一了,去年沟槽的国二第三名真是太难受了
标签:CI,include,smx,赛省赛,C++,蓝桥,int,now,define From: https://www.cnblogs.com/cjjsb/p/18138661