Codeforces Round 576 (Div. 1)

https://codeforces.com/contest/1199

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

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

A. MP3

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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)$的做法但是我直接拿线段树暴力模拟了。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#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$个点被标记了,剩下的就是独立集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#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}$表示当前矩形的答案,显然每次要么把这个矩形涂满要么剖成两半转移。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#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$大的话离散化之后就变成了有点权的点覆盖,可以用最小割做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#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)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#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;
}