2020-01-15 15:06阅读: 263评论: 0推荐: 1

Educational Codeforces Round 80 (Rated for Div. 2)

A. Deadline (CF 1288 A)

题目大意

给定n,d,问是否存在自然数x,使得x+dx+1n

解题思路

直接暴力。对于用了不等式的不会去证明其正确性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)

题目大意

给定A,B,问有对少个(a,b),其中a[1,A],b[1,B],使得a×b+a+b=a×10f(b)+b,其中f(x)表示x的位数。

解题思路

我们把式子化简,即为b+1=10f(b),很显然b只能为9,99,999这样的类型,我们设b+1cnt位,那答案就是cnt×A

神奇的代码
#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)

题目大意

给定n,m,要求构造两个数组a,b,记为(a,b),满足a,b数组有m个元素,且i[1,m],1aibin,问有多少个符合要求的(a,b)

解题思路

我们设dp0[i][j]表示当前第i位放j且不严格递增的方案数,则dp0[i][j]=k=1jdp0[i1][k],相应的再设个不严格递减的方案数dp1[i][j],最终答案就是i=1nj=indp0[m][i]×dp1[m][j]
对应为代码注释部分。

我们把b数组翻转过来,接到a后面,我们就得到了一个长度为2m的不严格递增的数组c,这样问题就是我们能够构造出多少个长度为2m的不严格递增的数组,当然也可以按照上面来次动态规划可以解决。不过可以证明,答案就是

C2m+n1n1

我们记cnti表示数i在数组c出现的次数,则有i=1ncnti=2×m,答案就是该方程组的非负整数解组的个数。这是个经典的排列组合问题,由于用隔板法要求解为正整数,而这里cnti可以为0,那么我们就等式左右两边都加一个n,并令CNTi=cnti+1,这样方程i=1nCNTi=2×m的解都是正整数,根据隔板法(假设有2m个球,插n1个板把求分为n组,每组的个数即为cnti,排序)答案就是C2m+n1n1

还有个证明方法就是我们构造一个d数组,其中di=ci+i,则2di2m+n,可以证明每个d数组恰与一个c数组对应并不会证明,注意到d数组数字各异,所以d数组共有C2m+n12m=C2m+n1n1

神奇的代码
#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)

题目大意

给定n个数组ai,每个数组有m个数,要求选择两组数组i,j,最大化min1km(max(aik,ajk))。输出i,j。注意:1n3×105,1m8

解题思路

我暴力就O(n2m)能怎么优化捏……但m是出奇的小。
注意到min的值可行性具有单调性,我们考虑如何快速判定它的可行性。
我们先二分最小值qwq,枚举了一组,我们要看能否存在另一组,把该组小于qwq的值变成大于等于qwq。那么对于一组数组的某一位,如果它大于等于就设为1,否则为0,这样我们就把某一组hash成一个数字numi,那么对于一组i,对应了一个数字numi,如果还有另一组j对应的数字numj两者|一下得到的数字是numi|numj2m1的话,这就说明这两组结合起来,它的最小值是大于等于qwq,而数字numj也就只有2m1255

于是我们就二分最小值,然后枚举一组,再枚举各位大于qwq的所有情况即可。时间复杂度O(n×2mlog2max(aij))

神奇的代码
#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)

题目大意

你有n(n3×105)个好友(这说明你与世界总人口的万分之一的人认识/滑稽),初始标号1n排列,然后你依次收到了m条短信,第i条短信是来自是ai,当收到第ai个好友发来的信息时,它会飞到列表的第一个并且在它前面的其他人都会后退一位,当然如果本身在第一位就什么事都没发生,除了你收到了条短信。问每个人位置最小和最大分别是多少。

解题思路

对于某个人i来说,如果它发过消息那么位置最小就是1,没有发过消息则是i,我们只用解决位置最大是多少。

对于某个人i,它第一次出现的时候,由于只有序号比它大的才会对它的位置有影响,那么它此时已经后退了b位,位居i+b位,这里b是在i前面,比j大且互不相同的数的个数。而当i第二次出现的时候,i后退的B位,这里B是在i第一次出现之后第二次出现之前的数互不相同的个数,之后的处理类似。注意到如果我们在数列里前面加上n,n1,n2,n3,...,1来初始化局面,那么在这之后对于i出现的情况的处理都类似i第二次出现了。

那么现在问题就转换成了统计区间内互不相同的个数。这个用Fenwick Tree或者Segment Tree来维护就好了。维护位置对个数的贡献,从左到右遍历,遇到第二次出现的就统计前后两次出现之间的位置对答案的贡献,然后把第一次出现的位置对答案的贡献置为0,第二次出现的位置对答案的贡献置为1,就能统计区间互不相同的个数了。

神奇的代码
#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)

题目大意

给定一个二分图,注意这里可能有重边,然后对边染色,染成红色费用r,染成蓝色费用b,对于一个点,如果连接它的红色边数量严格大于蓝色边,则这个点被染成红色;如果连接它的蓝色边数量严格大于红色边,则这个点被染成蓝色;否则它不被染色。现在给定点染色的要求URB,对应未染色,红色,蓝色,球给定一组边染色的方案,使得点被染成给定要求,且费用最小。

解题思路

玄学构图+费用流

神奇的代码
qwq


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/12196794.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(263)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.