隐藏页面特效

Codeforces Round 936 (Div. 2)

1|0Preface


懒狗闪总开完组会不打CF直接滚去睡觉了可海星,感觉我好像退化成我们队训练最少的人了

赛后补了下发现这场题竟然都会做,不过F不知道是我实现有问题常数大得一批加了读优才惊险卡过


2|0A. Median of an Array


签到,找到中位数后面与它相同的数的个数即可

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=100005; int t,n,a[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]); int ans=1; for (sort(a+1,a+n+1),i=(n+1)/2+1;i<=n;++i) if (a[i]==a[(n+1)/2]) ++ans; else break; printf("%d\n",ans); } return 0; }

3|0B. Maximum Sum


找出最大子段和S后,显然每次就是把S插入到能和原来的最大子段和一起统计的位置

最后答案就是(2k1)×S+i=1nai

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=200005,mod=1e9+7; int t,n,k,a[N]; inline int quick_pow(int x,int p=mod-2,int mul=1) { for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]); LL cur=0,mx=0,sum=0; for (i=1;i<=n;++i) cur=max(cur+a[i],0LL),mx=max(mx,cur),sum+=a[i]; printf("%d\n",(1LL*(quick_pow(2,k)-1)*(mx%mod)%mod+sum%mod+mod)%mod); } return 0; }

4|0C. Tree Cutting


最小值最大一眼二分答案x,考虑如何check

不妨选择任意一个点为根把树变成有根树,考虑从叶子往上贪心,如果存在某个点子树大小x就直接砍掉它和父亲的边

不难发现这样一定是最优的,最后检验下是否砍掉了k条边以及最后顶上留下的部分大小是否x即可

同时显然这个贪心过程对于任意点为根都能跑出正确的策略,因此可以随便选

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=100005; int t,n,k,x,y,cnt,all,sz[N]; vector <int> v[N]; inline void DFS(CI lim,CI now=1,CI fa=0) { if (cnt==k) return; sz[now]=1; for (auto to:v[now]) if (to!=fa) { DFS(lim,to,now); sz[now]+=sz[to]; if (cnt==k) return; } if (fa==0) return; if (sz[now]>=lim) ++cnt,all-=sz[now],sz[now]=0; if (cnt==k) return; } inline bool check(CI x) { all=n; cnt=0; DFS(x); return cnt==k&&all>=x; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) v[i].clear(); for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x); int l=1,r=n/(k+1),mid,ret; while (l<=r) if (check(mid=l+r>>1)) ret=mid,l=mid+1; else r=mid-1; printf("%d\n",ret); } return 0; }

5|0D. Birthday Gift


经典二进制操作,上来先考虑从高到低依次处理

不难发现对于某个二进制位2p,若序列中这一位为1的数有奇数个,则不管怎么划分最后这一位上一定为1

否则如果这一位为1的数有偶数个,只要把这些数偶数个分为一组即可,由于这题要求划分数量最多,因此显而易见的把相邻的两两合并即可

注意到在任意一个时刻,如果我们凑出了<x的数那么直接退出统计答案一定最优,否则如果前缀相等则还需要往后讨论后面的位带来的影响

暴力处理分组过程,复杂度O(nlogai)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=100005; int t,n,x,ans; inline void solve(vector <int> a,CI p=30) { if (p<0) return (void)(ans=max(ans,(int)a.size())); RI i,j; vector <int> pos; for (i=0;i<a.size();++i) if ((a[i]>>p)&1) pos.push_back(i); if (pos.size()%2==1&&((x>>p)&1)==0) return; if (pos.size()%2==1&&((x>>p)&1)==1) return solve(a,p-1); vector <int> rmv(a.size()); vector <int> b,c=a; for (i=0;i<pos.size();i+=2) for (j=pos[i]+1;j<=pos[i+1];++j) rmv[j]=1,c[pos[i]]^=c[j]; for (i=0;i<c.size();++i) if (!rmv[i]) b.push_back(c[i]); if (((x>>p)&1)==0) return solve(b,p-1); ans=max(ans,(int)b.size()); return solve(a,p-1); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; scanf("%d%d",&n,&x); vector <int> a(n); for (i=0;i<n;++i) scanf("%d",&a[i]); ans=-1; solve(a); printf("%d\n",ans); } return 0; }

6|0E. Girl Permutation


感觉算是我最近见到的最简单的计数题了,一眼秒的那种

首先如果前缀最大最后的位置和后缀最大第一个位置的值不同,则显然无解,同时还有些trivial的无解情况也一起顺手判掉

否则我们可以确定最大数n所在的位置x,首先需要一个把剩下的n1个数分到两边的方案数,就是Cn1x1

然后此时每一边的问题独立,以前缀这一段为例

我们从后往前枚举前缀最大值的位置,显然当前最大的数的位置唯一确定了,但该位置到x这一段中间的数方法是任意的

刚开始傻逼了这里乘了个组合数上去,后面过不了样例才发现原来是还得乘一个随便放顺序的排列数

后缀的处理同理,最后把每部分的方案数乘起来即可

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=200005,mod=1e9+7; int t,n,m1,m2,fact[N],ifac[N]; inline int quick_pow(int x,int p=mod-2,int mul=1) { for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul; } inline void init(CI n) { RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod; for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod; } inline int C(CI n,CI m) { if (n<0||m<0||n<m) return 0; return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod; } inline int A(CI n,CI m) { if (n<0||m<0||n<m) return 0; return 1LL*fact[n]*ifac[n-m]%mod; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t),init(2e5);t;--t) { RI i; scanf("%d%d%d",&n,&m1,&m2); vector <int> pre(m1),suf(m2); for (i=0;i<m1;++i) scanf("%d",&pre[i]); for (i=0;i<m2;++i) scanf("%d",&suf[i]); if (pre[0]!=1||suf[m2-1]!=n) { puts("0"); continue; } if (pre[m1-1]!=suf[0]) { puts("0"); continue; } int ans=C(n-1,suf[0]-1),cur; for (cur=suf[0]-1,i=m1-2;i>=0;--i) ans=1LL*ans*A(cur-1,pre[i+1]-pre[i]-1)%mod,cur-=pre[i+1]-pre[i]; for (cur=n-suf[0],i=1;i<m2;++i) ans=1LL*ans*A(cur-1,suf[i]-suf[i-1]-1)%mod,cur-=suf[i]-suf[i-1]; printf("%d\n",ans); } return 0; }

7|0F. Nobody is needed


很套路的一个题

首先对于只选一个数的情况特别考虑,然后我们发现对于某个固定的右端点,其合法的左端点选法是log级别的

因此如果我们把以l为开头,r为结尾的选法的方案数统计出来,最后就是个询问[L,R]包含的所有子区间的贡献和,这个是个经典的扫描线问题

现在的问题是如何计算l,r的贡献,这个也很好办,我们对于每个r按照l降序DP一下即可

具体实现看代码,复杂度O(nlog2n)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=1e6+5; int t,n,q,a[N],pos[N],ans[N],l,r; vector <int> frac[N]; vector <pi> evt[N]; #define Tp template <typename T> class FileInputOutput { private: static const int S=1<<21; #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++) #define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch)) char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[30]; public: inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; } Tp inline void read(T& x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); } Tp inline void write(T x,const char ch='\n') { if (x<0) x=-x,pc('-'); RI ptop=0; while (pt[++ptop]=x%10,x/=10); while (ptop) pc(pt[ptop--]+48); pc(ch); } inline void flush(void) { fwrite(Fout,1,Ftop-Fout,stdout); } #undef tc #undef pc }F; inline void init(CI n) { for (RI i=1;i<=n;++i) for (RI j=i;j<=n;j+=i) frac[j].push_back(i); } class Tree_Array { private: int bit[N]; public: #define lowbit(x) (x&(-x)) inline void init(CI n) { for (RI i=1;i<=n;++i) bit[i]=0; } inline void add(RI x,CI y) { for (;x;x-=lowbit(x)) bit[x]+=y; } inline int get(RI x,int ret=0) { for (;x<=n;x+=lowbit(x)) ret+=bit[x]; return ret; } #undef lowbit }BIT; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (F.read(t),init(1e6);t;--t) { RI i,j,k; for (F.read(n),F.read(q),i=1;i<=n;++i) F.read(a[i]),pos[a[i]]=i,evt[i].clear(); for (i=1;i<=n;++i) { vector <int> lpos; for (auto x:frac[a[i]]) if (pos[x]<i) lpos.push_back(pos[x]); vector <int> dp(lpos.size()); sort(lpos.begin(),lpos.end()); for (j=(int)lpos.size()-1;j>=0;--j) for (dp[j]=1,k=j+1;k<lpos.size();++k) if (a[lpos[k]]%a[lpos[j]]==0) dp[j]+=dp[k]; for (j=0;j<lpos.size();++j) evt[i].push_back({lpos[j],dp[j]}); } for (i=1;i<=q;++i) F.read(l),F.read(r),ans[i]=r-l+1,evt[r].push_back({l,-i}); for (BIT.init(n),i=1;i<=n;++i) for (auto [x,y]:evt[i]) if (y>0) BIT.add(x,y); else ans[-y]+=BIT.get(x); for (i=1;i<=q;++i) F.write(ans[i]," \n"[i==q]); } return F.flush(),0; }

8|0Postscript


接下来几天要忙CCPC Final的事情,估计没啥时间写题了的说


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18100024.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(141)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
历史上的今天:
2018-03-27 BZOJ 1001: [BeiJing2006]狼抓兔子
2018-03-27 Luogu P1231 教辅的组成
点击右上角即可分享
微信分享提示