暑假集训CSP提高模拟27
组题人: @KafuuChinocpp
\(T1\) P236.线性只因 \(100pts\)
-
诈骗题。
-
部分分
-
正解
- 记 \(opt\) 表示待选集合,统计 \(opt\) 中这一位为 \(1\) 的数并加入临时集合 \(tmp\) 。若 \(tmp\) 大小 \(\ge m\) 则加上这一位的贡献并将 \(opt\) 赋成 \(tmp\) 否则不管。
点击查看代码
ll a[1000010]; vector<ll>opt,tmp; int main() { ll n,m,ans=0,i,s; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; opt.push_back(i); } for(s=32;s>=0;s--) { tmp.clear(); for(i=0;i<opt.size();i++) { if((a[opt[i]]>>s)&1) { tmp.push_back(opt[i]); } } if(tmp.size()>=m) { opt=tmp; ans|=(1<<s); } } cout<<ans<<endl; return 0; }
\(T2\) P234.金箱子 \(30pts\)
-
若钓出来一个金箱子在模 \(99824435\) 意义下概率为 \(p_{i}\) ,则钓出来一个银箱子在模 \(99824435\) 意义下概率为 \(1-p_{i}+998244353\) 。
- 设钓出来一个金箱子的概率为 \(\frac{x}{y}\) ,由题有 \(\gcd(y,998244353)=1\) ,那么 \(\frac{x}{y}\) 在模 \(99824435\) 意义等于 \(p_{i}=x \times y^{998244353-2} \bmod 998244353\) ,相应的 \(\frac{y-x}{y}\) 在模 \(99824435\) 意义下等于 \((y-x) \times y^{998244353-2} \bmod998244353=(y^{998244353-1}-p_{i}+998244353) \bmod 998244353=(1-p_{i}+998244353) \bmod 998244353\) 。
-
部分分
- \(20pts\) :爆搜。
- \(10pts\) :观察到 \(k=1\) ,遂 \(\sum\limits_{i=1}^{n}p_{i}a_{i}+(1-p_{i})b_{i}\) 即为所求,
点击查看代码
const ll p=998244353; ll c[10010],a[10010],b[10010],ans=0; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } void dfs(ll pos,ll mul,ll num,ll n,ll k) { if(pos==n+1) { ans=(ans+mul*qpow(num,k,p))%p; } else { if(mul!=0) { dfs(pos+1,mul*c[pos]%p,(num+a[pos])%p,n,k); dfs(pos+1,mul*(1+p-c[pos])%p,(num+b[pos])%p,n,k); } } } int main() { ll n,k,i; cin>>n>>k; for(i=1;i<=n;i++) { cin>>c[i]>>a[i]>>b[i]; } if(k==1) { for(i=1;i<=n;i++) { ans=(ans+c[i]*a[i]%p+(1+p-c[i])*b[i]%p)%p; } } else { dfs(1,1,0,n,k); } cout<<ans<<endl; return 0; }
-
正解
\(T3\) P235.可持久化字符串 \(50pts\)
- \(2\) 操作实际上是新建一个版本为 \(a\) 的前一个副本。
- 部分分
-
\(50pts\) :哈希判等。大样例没过遂不保证代码正确性。
点击查看代码
const ll p=1000003579,base=13331; ll a[100010],pre[100010],hsh[100010],len[100010],num[100010],sum[100010],jc[100010]; char s[100010]; void sx_hash(char s[],ll len,ll a[]) { for(ll i=0;i<=len;i++) { a[i]=(i==0)?0:(a[i-1]*base%p+s[i])%p; } } ll ask_hash(ll l,ll r) { return (a[r]-a[l-1]*jc[r-l+1]%p+p)%p; } void solve(ll pos,ll n) { for(ll i=1;i+len[pos]-1<=n;i++) { num[pos]+=(ask_hash(i,i+len[pos]-1)==hsh[pos]); } } int main() { ll n,m,pd,x,l,r,c_cnt=0,i; char c; cin>>m>>n>>(s+1); sx_hash(s,n,a); for(i=0;i<=100000;i++) { jc[i]=(i==0)?1:jc[i-1]*base%p; } num[0]=sum[0]=1; for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>x>>c; c_cnt++; pre[c_cnt]=x; hsh[c_cnt]=(hsh[x]*base+c)%p; len[c_cnt]=len[x]+1; solve(c_cnt,n); sum[c_cnt]=sum[c_cnt-1]+num[c_cnt]; } if(pd==2) { cin>>x; c_cnt++; pre[c_cnt]=pre[pre[x]]; hsh[c_cnt]=hsh[pre[x]]; len[c_cnt]=len[pre[x]]; num[c_cnt]=num[pre[x]]; sum[c_cnt]=sum[c_cnt-1]+num[c_cnt]; } if(pd==3) { cin>>l>>r; if(l-1>=0) { cout<<sum[r]-sum[l-1]<<endl; } else { cout<<sum[r]<<endl; } } } return 0; }
-
- 正解
\(T4\) T3236.圣诞树 \(0pts\)
-
部分分
-
\(Subtask 1\) :主席树代替原来的桶,询问时枚举值域判断。
-
因 \(\operatorname{mex}\) 跑不满(数据没卡),不加 \(Subtask 3\) 的特判实测可以跑 \(70pts\) 。
点击查看代码
struct node { int nxt,to; }e[1000010]; int head[500010],a[500010],top[500010],fa[500010],siz[500010],dep[500010],son[500010],vis[500010],cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct PDS_SMT { int root[500010],rt_sum; struct SegmentTree { int ls,rs,sum,cnt; }tree[500010<<5]; #define lson(rt) tree[rt].ls #define rson(rt) tree[rt].rs int build_rt() { rt_sum++; return rt_sum; } void build_tree(int &rt,int l,int r) { rt=build_rt(); if(l==r) { return; } int mid=(l+r)/2; build_tree(lson(rt),l,mid); build_tree(rson(rt),mid+1,r); } void pushup(int rt) { tree[rt].cnt=tree[lson(rt)].cnt+tree[rson(rt)].cnt; } void update(int pre,int &rt,int l,int r,int pos) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum++; if(l==r) { tree[rt].cnt=1; return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos); } else { update(rson(pre),rson(rt),mid+1,r,pos); } pushup(rt); } int query1(int rt1,int rt2,int rt3,int rt4,int l,int r,int pos) { if(l==r) { return tree[rt1].sum+tree[rt2].sum-tree[rt3].sum-tree[rt4].sum; } int mid=(l+r)/2; if(pos<=mid) { return query1(lson(rt1),lson(rt2),lson(rt3),lson(rt4),l,mid,pos); } else { return query1(rson(rt1),rson(rt2),rson(rt3),rson(rt4),mid+1,r,pos); } } int query3(int rt1,int rt2,int rt3,int rt4,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt1].sum+tree[rt2].sum-tree[rt3].sum-tree[rt4].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query3(lson(rt1),lson(rt2),lson(rt3),lson(rt4),l,mid,x,y); } if(y>mid) { ans+=query3(rson(rt1),rson(rt2),rson(rt3),rson(rt4),mid+1,r,x,y); } return ans; } }T; void dfs1(int x,int father,int n) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; T.update(T.root[father],T.root[x],1,n,a[x]); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x,n); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]>dep[v])?v:u; } int query1(int u,int v,int n) { int rt=lca(u,v); for(int i=1;i<=n;i++) { if(T.query1(T.root[u],T.root[v],T.root[rt],T.root[fa[rt]],1,n,i)==0) { return i; } } return n+1; } int main() { int n,m,u,v,flag=1,i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); vis[a[i]]++; flag&=(vis[a[i]]==1); } for(i=1;i<=n-1;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs1(1,0,n); dfs2(1,1); for(i=1;i<=m;i++) { scanf("%d%d",&u,&v); printf("%d\n",query1(u,v,n)); } return 0; }
-
-
\(Subtask 3\) :因为每个数只出现一次,主席树维护一段值域内数的个数,询问时二分主席树或主席树上二分维护即可。
点击查看代码
struct node { int nxt,to; }e[1000010]; int head[500010],a[500010],top[500010],fa[500010],siz[500010],dep[500010],son[500010],vis[500010],cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct PDS_SMT { int root[500010],rt_sum; struct SegmentTree { int ls,rs,sum,cnt; }tree[500010<<5]; #define lson(rt) tree[rt].ls #define rson(rt) tree[rt].rs int build_rt() { rt_sum++; return rt_sum; } void build_tree(int &rt,int l,int r) { rt=build_rt(); if(l==r) { return; } int mid=(l+r)/2; build_tree(lson(rt),l,mid); build_tree(rson(rt),mid+1,r); } void pushup(int rt) { tree[rt].cnt=tree[lson(rt)].cnt+tree[rson(rt)].cnt; } void update(int pre,int &rt,int l,int r,int pos) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum++; if(l==r) { tree[rt].cnt=1; return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos); } else { update(rson(pre),rson(rt),mid+1,r,pos); } pushup(rt); } int query1(int rt1,int rt2,int rt3,int rt4,int l,int r,int pos) { if(l==r) { return tree[rt1].sum+tree[rt2].sum-tree[rt3].sum-tree[rt4].sum; } int mid=(l+r)/2; if(pos<=mid) { return query1(lson(rt1),lson(rt2),lson(rt3),lson(rt4),l,mid,pos); } else { return query1(rson(rt1),rson(rt2),rson(rt3),rson(rt4),mid+1,r,pos); } } int query3(int rt1,int rt2,int rt3,int rt4,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt1].sum+tree[rt2].sum-tree[rt3].sum-tree[rt4].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query3(lson(rt1),lson(rt2),lson(rt3),lson(rt4),l,mid,x,y); } if(y>mid) { ans+=query3(rson(rt1),rson(rt2),rson(rt3),rson(rt4),mid+1,r,x,y); } return ans; } }T; void dfs1(int x,int father,int n) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; T.update(T.root[father],T.root[x],1,n,a[x]); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x,n); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]>dep[v])?v:u; } int query1(int u,int v,int n) { int rt=lca(u,v); for(int i=1;i<=n;i++) { if(T.query1(T.root[u],T.root[v],T.root[rt],T.root[fa[rt]],1,n,i)==0) { return i; } } return n+1; } int query3(int u,int v,int n) { int rt=lca(u,v),l=1,r=n,mid,ans=n+1; while(l<=r) { mid=(l+r)/2; if(T.query3(T.root[u],T.root[v],T.root[rt],T.root[fa[rt]],1,n,1,mid)==mid) { l=mid+1; } else { r=mid-1; ans=mid; } } return ans; } int main() { int n,m,u,v,flag=1,i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); vis[a[i]]++; flag&=(vis[a[i]]==1); } for(i=1;i<=n-1;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs1(1,0,n); dfs2(1,1); if(flag==1) { for(i=1;i<=m;i++) { scanf("%d%d",&u,&v); printf("%d\n",query3(u,v,n)); } } else { for(i=1;i<=m;i++) { scanf("%d%d",&u,&v); printf("%d\n",query1(u,v,n)); } } return 0; }
-
-
正解
总结
- 感觉看到题后立刻想到一个自己没学过的知识点能解决这个问题,然后自己思路就被限制住了,接着就开始着急、开始恼,对心态有不少影响。或许要学着做到初三原政治老师说的到考场后忘掉自己学过和没学过的一切,而是重新分析题意了。
- \(T1\) 成功被题目名称和题目背景迷惑,以为线性基能处理按位与和的最大值,然后就没再管。最后打 \(m=2\) 部分分时发现 \(01Trie\) 维护不了,也就推翻了线性基的做法,然后就会维护从高到低维护待选集合的正解了,过了大样例后就交了。
- \(T2\) 推式子时把 \(1\) 漏掉了,最后重新来看的时候才发现了这个问题。
- \(T4\) 主席树向右子树递归把
update(rson(pre),rson(rt),mid+1,r,pos);
写成了update(rson(pre),rson(rt),l,mid,pos);
,挂了 \(70pts\) 。
后记
- 比赛一开始是 \(ACM/ICPC\) 赛制(但有部分分)然后又换成了 \(OI\) 赛制。
- \(T3\) 中途上传数据和小样例,第一次下发的大样例还出锅了。
- 不知道组题人怎么想的,会赛时改输入输出格式:标准输入输出->文件输入输出->标准输入输出。