隐藏页面特效

Codeforces Round 860 (Div. 2)

1|0Preface


两三天没写题了小小的补一下题

结果这场意外地很合胃口,1h不到就把A-E做完了,而且除了忘记初始化这种一眼丁真的错误好像也没写挂

可惜当时懒了周日晚上就不打了(主要去考模拟六级了回来太累了),不然狠狠地上分


2|0A. Showstopper


稍加观察就可以发现我们令所有的ai均小于等于bi再验证即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=105; int t,n,a[N],b[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]); for (i=1;i<=n;++i) if (scanf("%d",&b[i]),a[i]>b[i]) swap(a[i],b[i]); bool flag=1; for (i=1;i<n&&flag;++i) if (a[i]>a[n]) flag=0; for (i=1;i<n&&flag;++i) if (b[i]>b[n]) flag=0; puts(flag?"Yes":"No"); } return 0; }

3|0B. Three Sevens


一眼倒着做,每次随便找一个在之后没出现过的数并指定它为胜者即可

#include<cstdio> #include<iostream> #include<vector> #define RI register int #define CI const int& using namespace std; const int N=50005; int t,n,len[N],ans[N]; vector <int> v[N]; bool vis[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) for (scanf("%d",&len[i]),v[i].resize(len[i]),j=0;j<len[i];++j) scanf("%d",&v[i][j]),vis[v[i][j]]=0; int pos=-1; for (i=n;i;--i) { pos=-1; for (int x:v[i]) if (!vis[x]) pos=x; if (!~pos) { puts("-1"); break; } ans[i]=pos; for (int x:v[i]) vis[x]=1; } if (~pos) for (i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]); } return 0; }

4|0C. Candy Store


刚开始naive了想了一种合并糖果的方法,然后WA了一发后猛然悔悟

首先不难发现问题具有贪心性,对于固定的左端点,我们肯定是尽量地向右延伸区间

那么考虑如何验证一个区间[l,r]是否合法,不难发现设最后标价C至少是lcmlirbi的倍数

同时若假设我们令l=lcmlirbi,则对于i[l,r],均要满足lbi|ai,不难发现这等价于l|(ai×bi)

然后就很trivial了,我们再记一个g=gcdlir(ai×bi),然后只要看l是否为g的因子即可

#include<cstdio> #include<iostream> #define int long long #define RI register int #define CI const int& using namespace std; const int N=200005; int t,n,a[N],b[N]; inline int gcd(CI n,CI m) { return m?gcd(m,n%m):n; } inline int lcm(CI n,CI m) { return n/gcd(n,m)*m; } signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%lld",&t);t;--t) { RI i; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld%lld",&a[i],&b[i]); int g=0,l=1,ans=1; for (i=1;i<=n;++i) { l=lcm(l,b[i]); g=gcd(g,a[i]*b[i]); if (g%l) ++ans,l=b[i],g=a[i]*b[i]; } printf("%lld\n",ans); } }

5|0D. Shocking Arrangement


简单构造题,触发被动——构造题精通

首先不难发现右边是个定值,记这个极差为M,然后看到i=1nai=0这个限制,第一反应就是根据之前做过的某个构造题的结论,一定可以构造一个序列使得其前缀和数组的值始终大于0

再接着考虑一段子段和其实就是两个前缀和的差值,因此我们很容易想到只要让前缀和始终小于极差即可

具体的构造过程如下,我们先把正数和负数分别按绝对值从大到小排个序,然后每次加入一个还未处理的最大的正数

若此时整个序列的前缀和大于等于极差,则一直往里面放还未处理的最小的负数,只要其小于极差或者说明无解

这个做法的正确性显然,因为前缀和中最大值不可能超过极差,同时这般构造出的数列的前缀和一定都是大于等于0

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=300005,INF=1e9; int t,n,x,pos[N],neg[N],ans[N],cp,cn,lim,mi,mx; inline bool cmp(CI x,CI y) { return x>y; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j,cnt; for (scanf("%d",&n),mi=INF,mx=-INF,cp=cn=0,i=1;i<=n;++i) { scanf("%d",&x); mi=min(mi,x); mx=max(mx,x); if (x>=0) pos[++cp]=x; else neg[++cn]=x; } sort(pos+1,pos+cp+1,cmp); sort(neg+1,neg+cn+1); long long ret=0; for (lim=mx-mi,i=j=1,cnt=0;i<=cp;++i) { if ((ret+=pos[i])>=lim) { while (j<=cn&&ret>=lim) ans[++cnt]=neg[j],ret+=neg[j++]; if (ret>=lim) { cnt=-1; break; } } ans[++cnt]=pos[i]; } while (j<=cn) ans[++cnt]=neg[j++]; if (!~cnt) puts("No"); else for (puts("Yes"),i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]); } return 0; }

6|0E. Multitest Generator


首先不难发现答案肯定只有{0,1,2},因为我们只要把第一个数改成1然后把第二个数改成对应的长度就肯定可以构造出一个答案为2的解

同时答案为0的情况是很trivial的,那么现在问题就是如何判断aian的答案是否为1,大致可以分为两类:

  • 修改第一个数ai,此时需要满足ai+1an为连续的若干个test
  • 修改[i+1,n]中的某个数j使得aj=k,其实需要满足ai+1aj1为若干个(设为x个)test,且aj+k+1an为若干个(设为y个)test,且要求x+yai1

然后考虑如何实现,很容易想到用倒着用DP来维护:

  • gi表示某段后缀可以是否为若干个连续的test,若是则记录个数
  • mgi表示gi的后缀最大值,即mgi=maxijngj
  • fi表示以i开头,向后跳最多能得到多少个test(注意与gi的细微差别)
  • mxi表示以i开头,在向后跳了若干个test(设为x个)来到位置k后,x+mxk+1的最大值

其它的都很容易理解,就是这个mxi的定义看起来比较抽象,其实想一想很好理解

我们在计算上面的第二个条件时,所有的方案都可以被归结为两类,一类是从最后往前然后修改前面一部分的,一类是从前往后然后修改中间一部分的

而如果只用mgi的话就会遗漏掉修改中间某段的部分,因此需要定义这样一个奇怪的东西

不过一码归一码这些东西的转移都非常简单,看一下代码就一眼便知了的说

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=300005,INF=1e9; int t,n,a[N],g[N],mg[N],f[N],mx[N],ans[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]),g[i]=-1,f[i]=mg[i]=mx[i]=0; for (g[n+1]=f[n+1]=mg[n+1]=mx[n+1]=0,i=n;i;--i) { int t=i+a[i]+1; if (t<=n+1&&~g[t]) g[i]=g[t]+1; mg[i]=max(mg[i+1],g[i]); if (t<=n+1) f[i]=f[t]+1; if (t<=n+1) mx[i]=max(mx[t]+1,mg[t]+1); if (i==n) continue; if (~g[i+1]&&g[i+1]==a[i]) ans[i]=0; else if (~g[i+1]&&g[i+1]!=a[i]) ans[i]=1; else if (a[i]&&max(mg[i+1],mx[i+1])>=a[i]-1) ans[i]=1; else ans[i]=2; } for (i=1;i<n;++i) printf("%d%c",ans[i]," \n"[i==n-1]); } return 0; }

7|0F. Gifts from Grandfather Ahmed


其实看到题之后大概隐隐能猜到利用贪心的思想先做小的,但不知道正确性就不敢写

然后去看了Tutorial才发现要用一个叫Erdős–Ginzburg–Ziv theorem的东西,算是涨知识了

这个定理的大概意思就是对于任意正整数n,我们总可以在任意的2n1个正整数中找出n个时的它们的和是n的倍数

大致思路就是先证对于所有n为质数的情况,在证若n=p1n=p2时成立,则对于n=p1×p2亦成立,具体证明这里不赘述了看上面的论文吧

然后我们就很容易想到,我们把所有的si从小到大排序,然后我们分别按序处理s1sk1

不难发现当我们处理si时,剩下的boxes的数目为si+si+1++sk1,由于sksi,这个加和是一定大于等于2si1

因此我们总能对于前k1个class找到一种合法的分配,然后对于最后一个教室由于有一个可以随便分的,也是很trivial的

那么现在问题就是如何构造解了,显然可以通过一个很simple的DP来实现

fi,j,k表示在前i个数中,选出j个使得它们的和模si的余数为k的方案是否存在,然后由于又要考虑到构造方案,我们再设一个gi,j,k意义和fi,j,k类似但是要求强制选i这个数

因此总复杂度大概就是O(k×n3)的,不过DP的时候很多都跑不满(或许这个总复杂度可能就是O(n3)的?),也是能很轻松地跑过的

#include<cstdio> #include<iostream> #include<algorithm> #include<utility> #include<vector> #define RI register int #define CI const int& using namespace std; typedef pair <int,int> pi; const int N=205; int n,m,a[N],stk[N],top; pi s[N]; bool f[N][N][N],g[N][N][N],vis[N]; vector <int> ans[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j,k,r; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]); for (i=1;i<=m;++i) scanf("%d",&s[i].first),s[i].second=i; for (sort(s+1,s+m+1),i=1;i<m;++i) { int p=s[i].first,id=s[i].second; for (top=0,j=1;j<=2*p-1;++j) stk[++top]=a[n--]; for (j=0;j<=p;++j) for (k=0;k<p;++k) f[1][j][k]=g[1][j][k]=0; f[1][0][0]=f[1][1][stk[1]%p]=g[1][1][stk[1]%p]=1; for (j=2;j<=top;++j) for (k=0;k<=p;++k) for (r=0;r<p;++r) { f[j][k][r]=f[j-1][k][r]; if (k&&f[j-1][k-1][(r-stk[j]%p+p)%p]) f[j][k][r]=g[j][k][r]=1; else g[j][k][r]=0; } for (j=1;j<=top;++j) vis[j]=0; for (k=p,r=0,j=top;j;--j) if (g[j][k][r]) vis[j]=1,--k,r=(r-stk[j]%p+p)%p; for (j=1;j<=top;++j) if (vis[j]) ans[id].push_back(stk[j]); else a[++n]=stk[j]; } int sum=0; for (i=1;i<=n;++i) (sum+=a[i])%=(n+1); for (i=1;i<=n;++i) ans[s[m].second].push_back(a[i]); ans[s[m].second].push_back(n+1-sum); for (printf("%d\n",n+1-sum),i=1;i<=m;++i,putchar('\n')) for (int x:ans[i]) printf("%d ",x); return 0; }

8|0Postscript


本来这周三有下午场的CF,可惜我一个礼拜就那天有课打不了了

不过周五的晚间场还是可以尝试遛一遛的说(虽然第二天还有校赛的初赛,不过如果只是混个复赛资格应该问题不大的说)


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17271429.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(68)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2018-03-30 POJ 1459&&3436
点击右上角即可分享
微信分享提示