Educational Codeforces Round 80 (Rated for Div. 2)
A. Deadline (CF 1288 A)
题目大意
给定,问是否存在自然数,使得
解题思路
直接暴力。对于用了不等式的不会去证明其正确性qwq整除与上下取正什么的。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; typedef vector<PII> VPII; typedef vector<LL> VL; typedef pair<LL,LL> PLL; typedef vector<PLL> VPLL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } bool check(int n,int d){ for(int i=1;i<MIN(1e5,n);++i){ if (i+(int)ceil(d*1.0/(i+1))<=n) return true; } return false; } int main(void) { int kase; read(kase); for (int i = 1; i <= kase; i++) { // printf("Case #%d: ", i); int n,d; read(n); read(d); if (n>=d||check(n,d)) puts("YES"); else puts("NO"); } return 0; }
B. Yet Another Meme Problem (CF 1288 B)
题目大意
给定,问有对少个,其中,使得,其中表示的位数。
解题思路
我们把式子化简,即为,很显然b只能为这样的类型,我们设有位,那答案就是。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; typedef vector<PII> VPII; typedef vector<LL> VL; typedef pair<LL,LL> PLL; typedef vector<PLL> VPLL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int i = 1; i <= kase; i++) { LL A,B; read(A); read(B); LL qwq=9; int tmp=0; while(qwq<=B){ ++tmp; qwq=qwq*10+9; } LL ans=tmp*A; printf("%lld\n",ans); } return 0; }
C. Two Arrays (CF 1288 C)
题目大意
给定,要求构造两个数组,记为,满足数组有个元素,且,问有多少个符合要求的
解题思路
我们设表示当前第位放且不严格递增的方案数,则,相应的再设个不严格递减的方案数,最终答案就是
对应为代码注释部分。
我们把数组翻转过来,接到后面,我们就得到了一个长度为的不严格递增的数组,这样问题就是我们能够构造出多少个长度为的不严格递增的数组,当然也可以按照上面来次动态规划可以解决。不过可以证明,答案就是
我们记表示数在数组出现的次数,则有,答案就是该方程组的非负整数解组的个数。这是个经典的排列组合问题,由于用隔板法要求解为正整数,而这里可以为,那么我们就等式左右两边都加一个,并令,这样方程的解都是正整数,根据隔板法(假设有个球,插个板把求分为组,每组的个数即为,排序)答案就是。
还有个证明方法就是我们构造一个数组,其中,则,可以证明每个数组恰与一个数组对应并不会证明,注意到数组数字各异,所以数组共有。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; typedef vector<PII> VPII; typedef vector<LL> VL; typedef pair<LL,LL> PLL; typedef vector<PLL> VPLL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const LL mo=1e9+7; LL kuai(LL a,LL b){ LL qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; a=a*a%mo; b>>=1; } return qwq; } LL C(int n,int m){ LL qwq=1,tmp=1; for(int i=1;i<=n;++i) qwq=qwq*i%mo; for(int i=1;i<=m;++i) tmp=tmp*i%mo; qwq=qwq*kuai(tmp,mo-2)%mo; tmp=1; for(int i=1;i<=n-m;++i) tmp=tmp*i%mo; qwq=qwq*kuai(tmp,mo-2)%mo; return qwq; } int main(void) { int n,m; read(n); read(m); LL ans=C(2*m+n-1,n-1); /* LL dp0[m+1][n+1],dp1[m+1][n+1]; for(int i=1;i<=n;++i) dp0[1][i]=dp1[1][i]=1; for(int i=2;i<=m;++i){ LL sum=0; for(int j=1;j<=n;++j){ sum=(sum+dp0[i-1][j])%mo; dp0[i][j]=sum; } } for(int i=2;i<=m;++i){ LL sum=0; for(int j=n;j>=1;--j){ sum=(sum+dp1[i-1][j])%mo; dp1[i][j]=sum; } } LL ans=0; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) ans=(ans+dp0[m][i]*dp1[m][j]%mo)%mo; */ printf("%lld\n",ans); return 0; }
D. Minimax Problem (CF 1288 D)
题目大意
给定个数组,每个数组有个数,要求选择两组数组,最大化。输出。注意:。
解题思路
我暴力就能怎么优化捏……但是出奇的小。
注意到的值可行性具有单调性,我们考虑如何快速判定它的可行性。
我们先二分最小值,枚举了一组,我们要看能否存在另一组,把该组小于的值变成大于等于。那么对于一组数组的某一位,如果它大于等于就设为,否则为,这样我们就把某一组成一个数字,那么对于一组,对应了一个数字,如果还有另一组对应的数字两者一下得到的数字是是的话,这就说明这两组结合起来,它的最小值是大于等于,而数字也就只有。
于是我们就二分最小值,然后枚举一组,再枚举各位大于的所有情况即可。时间复杂度
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; typedef vector<PII> VPII; typedef vector<LL> VL; typedef pair<LL,LL> PLL; typedef vector<PLL> VPLL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=3e5+8; int n,m,x,y; pair<bool,int> sign[260]; int a[N][10],v[N]; bool check(int val){ memset(sign,0,sizeof(sign)); for(int i=0;i<n;++i){ int qwq=0; for(int j=0;j<m;++j) if (a[i][j]>=val) qwq|=(1<<j); sign[qwq]=make_pair(true,i); v[i]=qwq; } if (sign[(1<<m)-1].first==true){ x=y=sign[(1<<m)-1].second+1; return true; } for(int i=0;i<n;++i) for(int j=0;j<(1<<m);++j){ if (!sign[j].first) continue; int qwq=v[i]|j; if (qwq!=(1<<m)-1) continue; x=i+1; y=sign[j].second+1; return true; } return false; } int main(void) { read(n); read(m); int l=1e9+7,r=0; for(int i=0;i<n;++i) for(int j=0;j<m;++j){ read(a[i][j]); l=MIN(l,a[i][j]); r=MAX(r,a[i][j]); } x=y=0; ++r; while(l<r){ int mid=(l+r)>>1; if (check(mid)) l=mid+1; else r=mid; } printf("%d %d\n",x,y); return 0; }
E. Messenger Simulator (CF 1288 E)
题目大意
你有个好友(这说明你与世界总人口的万分之一的人认识/滑稽),初始标号排列,然后你依次收到了条短信,第条短信是来自是,当收到第个好友发来的信息时,它会飞到列表的第一个并且在它前面的其他人都会后退一位,当然如果本身在第一位就什么事都没发生,除了你收到了条短信。问每个人位置最小和最大分别是多少。
解题思路
对于某个人来说,如果它发过消息那么位置最小就是,没有发过消息则是,我们只用解决位置最大是多少。
对于某个人,它第一次出现的时候,由于只有序号比它大的才会对它的位置有影响,那么它此时已经后退了位,位居位,这里是在前面,比大且互不相同的数的个数。而当第二次出现的时候,后退的位,这里是在第一次出现之后第二次出现之前的数互不相同的个数,之后的处理类似。注意到如果我们在数列里前面加上来初始化局面,那么在这之后对于出现的情况的处理都类似第二次出现了。
那么现在问题就转换成了统计区间内互不相同的个数。这个用或者来维护就好了。维护位置对个数的贡献,从左到右遍历,遇到第二次出现的就统计前后两次出现之间的位置对答案的贡献,然后把第一次出现的位置对答案的贡献置为,第二次出现的位置对答案的贡献置为,就能统计区间互不相同的个数了。
神奇的代码
#include <bits/stdc++.h> #define MIN(a,b) ((((a)<(b)?(a):(b)))) #define MAX(a,b) ((((a)>(b)?(a):(b)))) #define ABS(a) ((((a)>0?(a):-(a)))) using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int,int> PII; typedef vector<PII> VPII; typedef vector<LL> VL; typedef pair<LL,LL> PLL; typedef vector<PLL> VPLL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N=6e5+8; struct BIT_TREE{ int cnt[N]; int lowbit(int x){ return (x&(-x)); } void updata(int pos,int tot,int val){ for(int i=pos;i<=tot;i+=lowbit(i)) cnt[i]+=val; } int sum(int pos){ int qwq=0; for(int i=pos;i>=1;i-=lowbit(i)) qwq+=cnt[i]; return qwq; } int query(int l,int r){ if (l>r) return 0; return sum(r)-sum(l-1); } }BIT; int main(void) { int n,m; read(n); read(m); int v[n+m+5]; for(int i=1;i<=n;++i) v[i]=n-i+1; for(int i=1;i<=m;++i) read(v[i+n]); int sign[n+m+5]={0}; int pos[n+5][2]={0}; for(int i=1;i<=n;++i) pos[i][0]=pos[i][1]=i; for(int i=1;i<=n+m;++i){ if (sign[v[i]]==0){ sign[v[i]]=i; BIT.updata(i,n+m,1); } else{ int qwq=BIT.query(sign[v[i]]+1,i-1)+1; pos[v[i]][0]=1; pos[v[i]][1]=MAX(pos[v[i]][1],qwq); BIT.updata(sign[v[i]],n+m,-1); BIT.updata(i,n+m,1); sign[v[i]]=i; } } for(int i=1;i<=n;++i) pos[i][1]=max(pos[i][1],BIT.query(sign[i]+1,n+m)+1); for(int i=1;i<=n;++i) printf("%d %d\n",pos[i][0],pos[i][1]); return 0; }
F. Red-Blue Graph (CF 1288 F)
题目大意
给定一个二分图,注意这里可能有重边,然后对边染色,染成红色费用,染成蓝色费用,对于一个点,如果连接它的红色边数量严格大于蓝色边,则这个点被染成红色;如果连接它的蓝色边数量严格大于红色边,则这个点被染成蓝色;否则它不被染色。现在给定点染色的要求,对应未染色,红色,蓝色,球给定一组边染色的方案,使得点被染成给定要求,且费用最小。
解题思路
玄学构图+费用流
神奇的代码
qwq
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/12196794.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步