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;
}