隐藏页面特效

think-cell Round 1

1|0Preface


这场一周前打的了,结果因为每天都有训练一直拖到今天才有时间补

前期虽然有点犯病但一直到D2出题都还算稳,然后看到E题经典数数题就走不动路了直接投降

后面请徐神来救场才堪堪会做,可惜最后推出优化的式子后比赛已经结束10min了

不够好在是手速够快没有掉分,感觉现在就是前面题出的巨快但总是后面题一个也做不来罚坐到结束


2|0A. Maximise The Score


签到,排序后相邻的两个数一对地取即可

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=105; 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),n<<=1,i=1;i<=n;++i) scanf("%d",&a[i]); int sum=0; for (sort(a+1,a+n+1),i=1;i<=n;i+=2) sum+=a[i]; printf("%d\n",sum); } return 0; }

3|0B. Permutation Printing


不难发现所有>n2的数都不存在倍数,因此将它们和n2的数交替放即可

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=100005; int t,n; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { vector <int> ans; scanf("%d",&n); for (RI i=1,j=n;i<=j;) { if (i<=j) ans.push_back(i++); if (i<=j) ans.push_back(j--); } for (auto x:ans) printf("%d ",x); putchar('\n'); } return 0; }

4|0C. Lexicographically Largest


感觉很典的一个题

首先如果所有ai+i都不相同就很简单,从大到小取即可,否则考虑有若干个ai+i的和都是x

我们总是先取最靠左的那个数,这样就可以一路得到x,x1,x2,

那么做法就很简单了,把所有ai+i从大到小排序,对于当前的数x,找到x且还未被使用的数y,将y输出即可

然后因为之前做过和这个基本一样的东西,直接离散化后用并查集维护一下就好了

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=600005; int t,n,a[N],fa[N],idx,rst[N]; map <int,int> ID; inline int getfa(CI x) { return fa[x]!=x?fa[x]=getfa(fa[x]):x; } 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]),a[i]+=i; idx=0; ID.clear(); vector <int> ans; for (sort(a+1,a+n+1,greater <int>()),i=1;i<=n;++i) { auto getID=[&](CI x) { if (ID.count(x)) return ID[x]; ID[x]=++idx; fa[idx]=idx; rst[idx]=x; return ID[x]; }; int x=getID(a[i]),cur=rst[getfa(x)]; ans.push_back(cur); fa[getfa(x)]=getfa(getID(cur-1)); } for (auto x:ans) printf("%d ",x); putchar('\n'); } return 0; }

5|0D1. Sum over all Substrings (Easy Version)


经典Guess Force,直接无脑猜结论就行

D1因为数据范围很小,可以暴力枚举每个子串,现在的问题就是对于某个确定的01串求它的贡献

直觉告诉我们如果包含该点的大的区间[L,R]内的众数为目标数字,那么被包含的较小的区间[l,r][L,R]中也必然存在合法区间,因此我们只需要考虑长度为2的区间即可

那么问题转化为,对于原01串的每个1,需要保证在与其距离1的位置内至少存在一个1,这个显然可以直接从前往后贪心

因此这题就有了O(n3)的做法,当然很容易优化到O(n2)但是没必要

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=105; int t,n; char s[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j; int ans=0; scanf("%d%s",&n,s+1); auto calc=[&](CI l,CI r) { int ret=0; static int tmp[N]; for (RI i=l;i<=r;++i) tmp[i]=-1; for (RI i=l;i<=r;++i) { if (tmp[i]!=-1) continue; if (s[i]=='1') { if (i-1>=l&&tmp[i-1]==1) { tmp[i]=0; continue; } tmp[i]=0; tmp[i+1]=1; ++ret; } else tmp[i]=0; } return ret; }; for (i=1;i<=n;++i) for (j=i;j<=n;++j) ans+=calc(i,j); printf("%d\n",ans); } return 0; }

6|0D2. Sum over all Substrings (Hard Version)


考虑上面那个东西的本质,其实就是要拿出最少的长度为3的区间,使得用这些区间能覆盖原01串中的所有1

不妨考虑DP,设fi表示所有以i为左端点的区间的答案之和,转移的时候考虑:

  • si=0,我们找到距离i最近的j(i<j),满足sj=1,显然第一个区间以j为左端点最优,因此fi=fj
  • si=1,那么显然第一个区间的左端点必然要放在i处,因此有fi=fi+3+(ni+1)(因为以i为左端点的串共有ni+1个)

直接从后往前递推处理即可,复杂度O(n)

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=1e6+5; int t,n,f[N]; char s[N]; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%lld",&t);t;--t) { RI i,j; int ans=0; scanf("%lld%s",&n,s+1); int nxt=n+1; for (f[n+1]=f[n+2]=f[n+3]=0,i=n;i>=1;--i) if (s[i]=='0') f[i]=f[nxt]; else f[i]=(n-i+1)+f[i+3],nxt=i; for (i=1;i<=n;++i) ans+=f[i]; printf("%lld\n",ans); } return 0; }

7|0E. 2..3...4.... Wonderful! Wonderful!


唉又被徐神救了,不然我真做不来一点Counting Problem

首先数据范围允许我们枚举k,然后大力枚举删除了d次,这里的复杂度是调和级数的

考虑用容斥,用选2dk个数删除的方案数Cn2dk减去不合法的删除方案数,考虑后者有什么性质

根据徐神大力猜测的结论,一个删除序列合法当且仅当存在至少一个没被删除的位置,使得其左右都有至少k个数被删除

证明的话考虑如果某个局面不满足上面的限制,则显然无法倒推完成最后一次删除操作;否则因为可以任意地把2k个数还原回去,因此条件一定变宽松了

那么考虑怎么计算当前局面的方案数,不难发现此时删除位置的形态一定形如中间一段连续的删除位置,然后两边分散一些删除位置随便放

具体地,不妨设len=2dk2(k1),则我们需要先选一段长度为len的区间全部钦定删除,然后在该区间的左右两侧各放上k1个删除位置

因此不难想到枚举中间那段的起始位置然后暴力将两边的方案数乘起来,即下面代码中注释的部分,但这样显然会TLE飞因此还需要考虑优化

不妨从组合意义的角度出发,这个问题相当于在nlen个球中先放一个隔板,再左右各取k1个球

不妨把隔板也看作一个球,这样相当于在nlen+1各球中取出2k2+1=2k1个球,因此最后方案数就是Cnlen+12k1,就可以O(1)计算这部分了

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=1e6+5,mod=998244353; int t,n,fact[N],ifac[N]; inline void inc(int& x,CI y) { if ((x+=y)>=mod) x-=mod; } inline void dec(int& x,CI y) { if ((x-=y)<0) x+=mod; } 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<m||n<0||m<0) return 0; return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t),init(1e6);t;--t) { RI i,d,k; for (scanf("%d",&n),k=1;k<=(n-1)/2;++k) { int ans=1; for (d=1;2*d*k<=n;++d) { inc(ans,C(n,2*d*k)); int len=2*d*k-(2*k-2); //for (i=k;n-(i+len-1)>=k-1;++i) //dec(ans,1LL*C(i-1,k-1)*C(n-(i+len-1),k-1)%mod); dec(ans,C(n-len+1,2*k-1)); } printf("%d ",ans); } putchar('\n'); } return 0; }

8|0F. Maximize the Difference


这题其实本质不难,但就是要破除一定要用数据结构来优化的桎梏

首先max[(x|z)(y|z)]是一个很经典的问题,设其最大值为w,则一定满足wx的子集,同时w也是y的补集的子集

证明的话也很简单,wx的子集这点是由于若x某一位是0w这一位上就不可能是1;同理若y某位上是1w这一位上就必为0,因此wy的补集的子集

因此不难想到一个暴力做法,开两个数组分别记录当前状态是否作为某个数的子集/补集的子集出现过,每加入一个数字的时候暴力枚举子集/补集的子集即可,这样复杂度是O(322)的,无法通过

考虑我们只要算出最大值,因此如果已经确定一个数是合法的(其同时作为某个数的子集以及某个数补集的子集出现过),那么它的所有子集就没有必要再扩展了

因此可以用类似BFS的方法来按位扩展子集,一旦遇到以及扩展过的状态就不再重复扩展,最终复杂度就变成O(222×22)

#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 __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=(1<<22)+5; int t,n,q,x,ans,vis[N][2]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j; scanf("%d%d",&n,&q); ans=0; int m=0; while ((1<<m)<n) ++m; for (i=0;i<(1<<m);++i) for (j=0;j<2;++j) vis[i][j]=0; while (q--) { scanf("%d",&x); x=(x+ans)%n; for (i=0;i<2;++i) { int now=(i==0?x:x^((1<<m)-1)); if (vis[now][i]) continue; vis[now][i]=1; queue <int> q; q.push(now); while (!q.empty()) { now=q.front(); q.pop(); if (vis[now][i^1]) ans=max(ans,now); for (j=0;j<m;++j) if ((now>>j)&1) if (!vis[now^(1<<j)][i]) vis[now^(1<<j)][i]=1,q.push(now^(1<<j)); } } printf("%d ",ans); } putchar('\n'); } return 0; }

9|0Postscript


唉马上就要开学力,事情多起来后训练的时间就要减少了

而且这个学期还有给新生暑假前集训出题、校赛、省赛、蓝桥杯、天梯赛等一堆事情,同时绩点和科研啥的也不能落下

借最近玩的《拔作岛》的经典台词与诸君共勉吧:

不畏人间苦,不惧世上难,万般皆磨炼,有志终逞愿。


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18031005.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(168)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2018-02-24 POJ 2299
2018-02-24 EZ 2018 1 21 2018noip第五次膜你赛
2018-02-24 POJ 1068&&2632&&1573&&2993&&2996
点击右上角即可分享
微信分享提示