Codeforces Round #576 (Div. 1)

Codeforces Round #576 (Div. 1)

https://codeforces.com/contest/1199

虽然我打的是\(\rm Div. ~2\)但是因为\(\rm Div. ~2\)前两题实在是太水了这里就不说了。。

不过涨了一百来$\rm rating $还行(我果然分还是太低了)

A. MP3

算出可以接受的最大种类数然后对着题意模拟吧。。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
#define lf double
 
#define pii pair<int,int >
#define vec vector<int >
 
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
 
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
 
const int maxn = 2e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
 
int t[maxn],a[maxn],r[maxn],n,m,pre[maxn],suf[maxn];
 
int main() {
    read(n),read(m),m<<=3;
    for(int i=1;i<=n;i++) read(a[i]),r[i]=a[i];
    sort(r+1,r+n+1);int l=unique(r+1,r+n+1)-r-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(r+1,r+l+1,a[i])-r,t[a[i]]++;
    for(int i=1;i<=n;i++) pre[i]=pre[i-1]+t[i];
    for(int i=n;i;i--) suf[i]=suf[i+1]+t[i];
    if(m/n>=20||(1<<(m/n))>=l) {return puts("0"),0;}
    int k=1<<(m/n),ans=1e9;
    for(int i=1;i<=l;i++)
        ans=min(ans,pre[i-1]+suf[i+k]);
    write(ans);
    return 0;
}

B. Welfare State

好像有\(O(n)\)的做法但是我直接拿线段树暴力模拟了。。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
#define lf double
 
#define pii pair<int,int >
#define vec vector<int >
 
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
 
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
 
const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
 
int n,a[maxn],q;
 
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
 
struct Segment_Tree {
    int tag[maxn],mn[maxn];
 
    void push_tag(int p,int v) {
        // printf("push_tag :: %d %d\n",p,v);
        if(tag[p]>=v) return ;
        if(mn[p]>=v) return ;
        tag[p]=v,mn[p]=v;
    }
 
    void pushdown(int p) {
        if(tag[p]==-1) return ;
        push_tag(ls,tag[p]),push_tag(rs,tag[p]);
        tag[p]=-1;
    }
 
    void cover(int p,int l,int r,int x,int y,int v) {
        if(mn[p]>=v) return ;
        if(x<=l&&r<=y) return push_tag(p,v),void();
        pushdown(p);
        if(x<=mid) cover(ls,l,mid,x,y,v);
        if(y>mid) cover(rs,mid+1,r,x,y,v);
        mn[p]=min(mn[ls],mn[rs]);
    }
 
    void modify(int p,int l,int r,int x,int v) {
        if(l==r) return tag[p]=-1,mn[p]=v,void();
        pushdown(p);
        if(x<=mid) modify(ls,l,mid,x,v);
        else modify(rs,mid+1,r,x,v);
        mn[p]=min(mn[ls],mn[rs]);
    }
 
    void print(int p,int l,int r) {
        if(l==r) printf("%d ",mn[p]);
        else pushdown(p),print(ls,l,mid),print(rs,mid+1,r);
    }
 
    void build(int p,int l,int r) {
        tag[p]=-1;
        if(l==r) mn[p]=a[l];
        else build(ls,l,mid),build(rs,mid+1,r),mn[p]=min(mn[ls],mn[rs]);
    }
 
    void debug(int p,int l,int r) {
        printf("debug :: %d %d %d %d\n",l,r,tag[p],mn[p]);
        if(l==r) return ;
        else debug(ls,l,mid),debug(rs,mid+1,r);
    }
}T;
 
int main() {
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    T.build(1,1,n);
    read(q);
    while(q--) {
        int op,x,y;read(op);
        if(op==1) read(x),read(y),T.modify(1,1,n,x,y);
        else read(x),T.cover(1,1,n,1,n,x);
        // printf("----------\n");
        // T.debug(1,1,n);
    }T.print(1,1,n);puts("");
    return 0;
}

C. Matching vs Independent Set

我们把边全连起来同时暴力的找到一个匹配,也就是每次判当前这条边能不能用,如果能就打个标记顺便标记下两个点。

那么如果匹配数\(\geqslant n\)就做完了,否则一定只有小于等于\(2n\)个点被标记了,剩下的就是独立集。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
#define lf double
 
#define pii pair<int,int >
#define vec vector<int >
 
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
 
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
 
const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
 
int d[maxn],c[maxn],n,m;
 
void solve() {
    read(n),read(m);int t=0;
    for(int i=1;i<=m;i++) {
        int x,y;read(x),read(y);
        if(!d[x]&&!d[y]) d[x]++,d[y]++,c[i]=1,t++;
    }
    if(t>=n) {
        puts("Matching");
        for(int i=1,p=1;p<=n;i++)
            if(c[i]) printf("%d ",i),p++;
    } else {
        puts("IndSet");
        for(int i=1,p=1;p<=n;i++)
            if(!d[i]) printf("%d ",i),p++;
    }puts("");
}
 
void clear() {
    for(int i=1;i<=3*n;i++) d[i]=0;
    for(int i=1;i<=m;i++) c[i]=0;
}
 
int main() {
    int t;read(t);while(t--) solve(),clear();
    return 0;
}

D. Rectangle Painting 1

大水题。。直接\(dp\)就好了。

\(f_{x1,y1,x2,y2}\)表示当前矩形的答案,显然每次要么把这个矩形涂满要么剖成两半转移。

我写了个记搜结果跑的好慢啊。。。不过记搜是真的好写。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
#define lf double
 
#define pii pair<int,int >
#define vec vector<int >
 
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
 
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
 
const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
 
char c[52];
int f[52][52][52][52],s[52][52],n;
 
int dfs(int x1,int y1,int x2,int y2) {
    int &r=f[x1][x2][y1][y2];
    if(x1==x2&&y1==y2) return r=s[x1][y1];
    if(r<1e9) return r;
    r=max(x2-x1+1,y2-y1+1);
    for(int i=x1;i<=x2-1;i++)
        r=min(r,dfs(x1,y1,i,y2)+dfs(i+1,y1,x2,y2));
    for(int i=y1;i<=y2-1;i++)
        r=min(r,dfs(x1,y1,x2,i)+dfs(x1,i+1,x2,y2));
    // printf("dfs ::  %d %d %d %d %d\n",x1,y1,x2,y2,r);
    return r;
}
 
int main() {
    read(n);
    for(int i=1;i<=n;i++) {
        scanf("%s",c+1);
        for(int j=1;j<=n;j++) s[i][j]=c[j]=='#';
    }memset(f,63,sizeof f);
    write(dfs(1,1,n,n));
    return 0;
}

E. Rectangle Painting 2

这差不多就是原题了吧。。可以参考一下:[HNOI2013]消毒。

思想就是每次消一整行或一整列,那么如果\(n\)不大,我们可以建二分图,左边表示每行,右边是列,每个黑点就弄一条边连向对应行列,答案就是最小点覆盖。

\(n\)大的话离散化之后就变成了有点权的点覆盖,可以用最小割做。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
#define lf double
 
#define pii pair<int,int >
#define vec vector<int >
 
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
 
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
 
const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
 
int n,m,head[maxn],tot=1,c[102][102],rx[maxn],ry[maxn],s,t,dis[maxn];
struct edge{int to,nxt,w;}e[maxn<<1];
struct data {int x,y,xx,yy;}a[maxn];
 
void prepare(int *r) {  
    r[++r[0]]=0,r[++r[0]]=n;
    sort(r+1,r+r[0]+1);r[0]=unique(r+1,r+r[0]+1)-r-1;
    // for(int i=1;i<=r[0];i++) printf("%d ",r[i]);puts("");
}
 
int get(int x,int *r) {
    return lower_bound(r+1,r+r[0]+1,x)-r;
}
 
void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
void ins(int u,int v,int w) {add(u,v,w),add(v,u,0);}
 
int bfs() {
    queue<int > q;memset(dis,-1,sizeof dis);
    q.push(s);dis[s]=0;
    while(!q.empty()) {
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].nxt)
            if(e[i].w>0&&dis[e[i].to]==-1) {
                dis[e[i].to]=dis[x]+1;
                if(e[i].to==t) return 1;
                q.push(e[i].to);
            }
    }return 0;
}
 
int dfs(int x,int f) {
    if(x==t) return f;
    int used=0;
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].w>0&&dis[e[i].to]==dis[x]+1) {
            int d=dfs(e[i].to,min(f,e[i].w));
            e[i].w-=d,e[i^1].w+=d,used+=d,f-=d;
            if(!f) break;
        }
    if(!used) dis[x]=-1;
    return used;
}
 
int max_flow() {
    int flow=0;
    while(bfs()) flow+=dfs(s,inf);
    return flow;
}
 
int main() {
    read(n),read(m);
    for(int i=1;i<=m;i++) {
        read(a[i].x),read(a[i].y),read(a[i].xx),read(a[i].yy);
        rx[++rx[0]]=a[i].x-1,rx[++rx[0]]=a[i].xx;
        ry[++ry[0]]=a[i].y-1,ry[++ry[0]]=a[i].yy;
    }
    prepare(rx),prepare(ry);s=rx[0]+ry[0]+3,t=s+1;
    for(int i=1;i<rx[0];i++) ins(s,i,rx[i+1]-rx[i]);
    for(int i=1;i<ry[0];i++) ins(i+rx[0],t,ry[i+1]-ry[i]);
    for(int i=1;i<=m;i++) {
        a[i].x=get(a[i].x-1,rx),a[i].xx=get(a[i].xx,rx);
        a[i].y=get(a[i].y-1,ry),a[i].yy=get(a[i].yy,ry);
        for(int k=a[i].x+1;k<=a[i].xx;k++)
            for(int l=a[i].y+1;l<=a[i].yy;l++)
                c[k-1][l-1]=1;//,printf("%d %d\n",k-1,l-1);
    }for(int i=1;i<rx[0];i++)
         for(int j=1;j<ry[0];j++)
             if(c[i][j]) ins(i,j+rx[0],inf);
    write(max_flow());
    return 0;
}

F. GCD Groups 2

大神题。。我弄了挺久才弄明白。

首先注意到一个数最多只有\(k=9\)个质因子。

假设我们现在知道了\(a,b\)在不同的集合,那么我们可以考虑消除每个质因子的影响。

那么可以直接\(dp\),设\(f_{s,t}\)表示左边集合\(k\)个质因子状态为\(s\),其中\(0/1\)表示有没有被消除,右边为\(t\)

那么每次枚举状态,枚举当前数放左边还是右边就好了。

这样\(dp\)复杂度是\(O(2^{2k}\cdot n)\)的。

注意到我们一个集合至多只有\(k+1\)个有用的数,也就是说我们可以把其中的一个集合缩减到\(k+1\)个数,其他的扔到另一个集合里,方案仍然合法。

所以如果我们任选\(a,b\),错误的概率为\(\frac{k+1}{n}\)

所以我们直接随机化这个算法,期望只需要做\(O(\frac{n}{n-k+1})\)次,因为\(k\)是常数所以这就是\(O(1)\)次。

那么我们得到了一个\(O(2^{2k}\cdot n)\)做法。

考虑怎么优化,显然对于一个质因子至多只有\(k\)个数是有用的,那么一共只有\(k^2\)个数有用,剩下的都可以随便放。

所以总复杂度为\(O(2^{2k}\cdot k^2+n)\)

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
#define lf double
 
#define pii pair<int,int >
#define vec vector<int >
 
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
 
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
 
const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
 
int r[maxn],n,f[1100][1100],tt[maxn],w[2][1000],c[2][1000];
struct data {int s,t,w;}pre[1100][1100];
 
void factorize(int x,int *a) {
    memset(a,0,(sizeof x)*20);
    for(int i=2;i*i<=x;i++) {
        if(x%i) continue;
        a[++a[0]]=i;
        while(x%i==0) x/=i;
    }if(x!=1) a[++a[0]]=x;
}
 
int p[maxn];
 
void prepare(int a,int b) {
    memset(c,0,sizeof c);
    memset(w,0,sizeof w);
    memset(p,0,sizeof p);
    factorize(r[a],w[0]),factorize(r[b],w[1]);
    // printf("factor ");for(int i=1;i<=w[0][0];i++) printf("%d ",w[0][i]);puts("");
    // printf("factor ");for(int i=1;i<=w[1][0];i++) printf("%d ",w[1][i]);puts("");
    for(int i=1;i<=n;i++) {
        if(i==a||i==b) continue;int bo=0;
        for(int o=0;o<=1;o++) {
            for(int k=1;k<=w[o][0];k++)
                if(r[i]%w[o][k]) {
                    if(c[o][k]==w[o^1][0]) continue;
                    c[o][k]++,bo=1,p[++p[0]]=i;
                    break;
                }
            if(bo) break;
        }
    }
}
 
int dp(int a,int b) {
    prepare(a,b);
    memset(tt,0,sizeof tt);
    memset(f,0,sizeof f);
    int k1=w[0][0],k2=w[1][0];
    f[(1<<k1)-1][(1<<k2)-1]=1;tt[a]=1,tt[b]=2;
    // for(int i=1;i<=p[0];i++) printf("%d ",p[i]);puts("");
    for(int i=1;i<=p[0];i++) {
        int s1=(1<<k1)-1,t1=(1<<k2)-1;
        for(int j=1;j<=k1;j++) if(r[p[i]]%w[0][j]) s1^=1<<(j-1);
        for(int j=1;j<=k2;j++) if(r[p[i]]%w[1][j]) t1^=1<<(j-1);
        for(int s=0;s<1<<k1;s++)
            for(int t=0;t<1<<k2;t++) {
                if(!f[s][t]) continue;
                if(!f[s&s1][t]) f[s&s1][t]=1,pre[s&s1][t]=(data){s,t,p[i]};
                if(!f[s][t&t1]) f[s][t&t1]=1,pre[s][t&t1]=(data){s,t,p[i]};
            }
    }if(!f[0][0]) return 0;
    int s=0,t=0;
    while(!(s==(1<<k1)-1&&t==(1<<k2)-1)) {
        data d=pre[s][t];
        if(s!=d.s) tt[d.w]=1;
        else tt[d.w]=2;
        s=d.s,t=d.t;//printf("get :: %d %d %d\n",s,t,d.w);
    }return 1;
}
 
int main() {
    read(n);srand(time(0));
    for(int i=1;i<=n;i++) read(r[i]);
    for(int T=1;T<=10;T++) {
        int a=rand()%n+1,b=rand()%n+1;
        if(a==b) continue;//printf("fuckpps :: %d %d\n",a,b);
        if(!dp(a,b)) continue;
        puts("YES");
        for(int i=1;i<=n;i++) printf("%d ",tt[i]?tt[i]:1);
        return 0;
    }puts("NO");
    return 0;
}
posted @ 2019-08-27 16:05  Hyscere  阅读(144)  评论(0编辑  收藏