zoukankan      html  css  js  c++  java
  • Codeforces Round #576 (Div. 1)

    Preface

    闲来无事打打CF,就近找了场Div1打打

    这场感觉偏简单,比赛时艹穿的人都不少,也没有3000+的题

    两三个小时就搞完了吧(F用随机水过去了)


    A. MP3

    题意不好理解,没用翻译看了好久然后就看错了,后来发现是个SB题

    考虑我们将数字排序+离散化之后就可以知道一个区间外有多少个数,可以直接计算答案

    然后发现这个区间的移动有单调性,因此直接two points扫一下就好了

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=400005;
    int n,lim,num,log_[N],a[N],rst[N],L[N],R[N],ans=1e9;
    int main()
    {
    	RI i,pos; for (scanf("%d%d",&n,&lim),lim*=8,i=1;i<=n;++i)
    	scanf("%d",&a[i]),rst[i]=a[i],log_[i]=ceil(log2(i));
    	sort(a+1,a+n+1); sort(rst+1,rst+n+1); num=unique(rst+1,rst+n+1)-rst-1;
    	for (i=1;i<=n;++i) R[a[i]=lower_bound(rst+1,rst+num+1,a[i])-rst]=i;
    	for (i=n;i;--i) L[a[i]]=i; for (i=pos=1;i<=num;++i)
    	{
    		while (pos<=i&&1LL*log_[i-pos+1]*n>lim) ++pos;
    		ans=min(ans,L[pos]-1+n-R[i]);
    	}
    	return printf("%d",ans),0;
    }
    

    B. Welfare State

    题目大意就是单点修改和全局取(max)(将所有数与一个数取(max)),输出最后的序列

    理解题意就很简单,我们记录下一个点最后一次被单点修改的值与时间,然后在所有全局取(max)的操作中找一个时间在它后面的最大值尝试更新这个值即可

    后缀(max)+二分即可完成

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=200005;
    int n,x,q,opt,y,tim[N],lst[N],et[N],val[N],mx[N],tot;
    int main()
    {
    	//freopen("B.in","r",stdin); freopen("B.out","w",stdout);
    	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&lst[i]);
    	for (scanf("%d",&q),i=1;i<=q;++i)
    	{
    		scanf("%d%d",&opt,&x); if (opt^1) et[++tot]=i,val[tot]=x;
    		else scanf("%d",&y),tim[x]=i,lst[x]=y;
    	}
    	for (i=tot;i;--i) mx[i]=max(mx[i+1],val[i]); for (i=1;i<=n;++i)
    	printf("%d ",max(lst[i],mx[lower_bound(et+1,et+tot+1,tim[i])-et]));
    	return 0;
    }
    

    C. Matching vs Independent Set

    思博题,我都想了些什么鬼东西啊

    考虑那(3n)个点一定有着什么限制,那么我们考虑以下算法:

    1. 扫一遍所有边,若这条边的两个端点都没有被匹配过就匹配这条边,记此时匹配过的边数为(cnt)
    2. (cntge n)则得出了一个合法匹配
    3. 否则在剩下的点中任取(n)个就是独立集

    考虑这个算法的正确性,显然当(cntge n)时能得出合法匹配,故若(cnt<n),则剩下的两两无边相连的点数就是(3n-2cdot cnt>n),因此一定能得到答案

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=3e5+5;
    int t,n,m,x,y,eg[N],cnt; bool vis[N];
    int main()
    {
    	for (scanf("%d",&t);t;--t)
    	{
    		RI i; for (scanf("%d%d",&n,&m),cnt=0,i=1;i<=m;++i)
    		scanf("%d%d",&x,&y),!vis[x]&&!vis[y]&&(vis[x]=vis[y]=1,eg[++cnt]=i);
    		if (cnt>=n)
    		{
    			puts("Matching"); for (i=1;i<=n;++i)
    			printf("%d%c",eg[i]," 
    "[i==cnt]);
    		} else
    		{
    			puts("IndSet"); for (cnt=0,i=1;i<=3*n&&cnt<n;++i)
    			if (!vis[i]) printf("%d ",i),++cnt; putchar('
    ');
    		}
    		for (i=1;i<=3*n;++i) vis[i]=0;
    	}
    	return 0;
    }
    

    D. Rectangle Painting 1

    首先我们考虑两个显而易见的结论:

    1. 若一个子矩阵全为白显然无需染色
    2. 若一个子矩阵全为黑直接染完是最优的(即拆成小块永远没有直接染优)

    那么我们把这两种情况作为DP的终止条件就可以DP了,设(f_{x1,y1,x2,y2})表示((x1,y1)-(x2,y2))的子矩阵全部染白的最小代价,转移的之后考虑断开行或列分割成两个更小的矩阵转移即可

    记忆化搜索实现DP,二维前缀和处理下矩阵状态即可

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=55;
    int n,pt[N][N],f[N][N][N][N]; char ch;
    inline void get_char(char& ch)
    {
    	while (isspace(ch=getchar()));
    }
    inline int getpt(CI x1,CI y1,CI x2,CI y2)
    {
    	return pt[x2][y2]-pt[x2][y1-1]-pt[x1-1][y2]+pt[x1-1][y1-1];
    }
    inline int DP(CI x1,CI y1,CI x2,CI y2)
    {
    	if (~f[x1][y1][x2][y2]) return f[x1][y1][x2][y2];
    	if (getpt(x1,y1,x2,y2)==(x2-x1+1)*(y2-y1+1))
    	return f[x1][y1][x2][y2]=max(x2-x1+1,y2-y1+1);
    	if (!getpt(x1,y1,x2,y2)) return f[x1][y1][x2][y2]=0;
    	RI i; int ret=max(x2-x1+1,y2-y1+1);
    	for (i=x1;i<x2;++i) ret=min(ret,DP(x1,y1,i,y2)+DP(i+1,y1,x2,y2));
    	for (i=y1;i<y2;++i) ret=min(ret,DP(x1,y1,x2,i)+DP(x1,i+1,x2,y2));
    	return f[x1][y1][x2][y2]=ret;
    }
    int main()
    {
    	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) for (j=1;j<=n;++j)
    	get_char(ch),pt[i][j]=pt[i-1][j]+pt[i][j-1]-pt[i-1][j-1]+(ch=='#');
    	return memset(f,-1,sizeof(f)),printf("%d",DP(1,1,n,n)),0;
    }
    

    E. Rectangle Painting 2

    和上面一题类似,只不过把数据范围加大了,然后代价变成了取(min)

    又有一个显而易见的结论此时每次选择的都是一整行或一整列,代价为(1)

    因此我们考虑将每个黑点的行节点向列节点连一条流量为(infty)的边,然后从源点向行节点连流量为(1)的边,从列节点向汇点连流量为(1)的边,这样整张图的最小割即为答案(至少要割去一个黑点的行或列节点)

    但是现在黑点给出的是一个矩阵的形式,这也很简单,我们将区间化为左开右闭,然后离散化一下,建图的时候源点到行节点连接这一段经过行的数目,到汇点的同理

    然后跑一下最大流就是答案

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=205,M=200005,INF=1e9;
    int n,m,l1[N],r1[N],l2[N],r2[N],rstl[N],rstr[N],numl,numr;
    struct edge
    {
    	int to,nxt,v;
    }e[M]; int head[N],cnt=1,s,t;
    inline int findl(CI x)
    {
    	return lower_bound(rstl+1,rstl+numl+1,x)-rstl;
    }
    inline int findr(CI x)
    {
    	return lower_bound(rstr+1,rstr+numr+1,x)-rstr;
    }
    inline void addedge(CI x,CI y,CI v)
    {
    	e[++cnt]=(edge){y,head[x],v}; head[x]=cnt;
    	e[++cnt]=(edge){x,head[y],0}; head[y]=cnt;
    }
    #define to e[i].to
    class Network_Flow
    {
    	private:
    		int q[N],dep[N],cur[N];
    		inline bool BFS(CI s,CI t)
    		{
    			RI H=0,T=1; memset(dep,0,t+1<<2); q[dep[s]=1]=s;
    			while (H<T)
    			{
    				int now=q[++H]; for (RI i=head[now];i;i=e[i].nxt)
    				if (e[i].v&&!dep[to]) dep[to]=dep[now]+1,q[++T]=to;
    			}
    			return dep[t];
    		}
    		inline int DFS(CI now,CI tar,int dis)
    		{
    			if (now==tar) return dis; int ret=0;
    			for (RI& i=cur[now];i;i=e[i].nxt)
    			if (e[i].v&&dep[to]==dep[now]+1)
    			{
    				int dist=DFS(to,tar,min(dis,e[i].v));
    				dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist;
    			}
    			if (ret) dep[now]=0; return ret;
    		}
    	public:
    		inline int Dinic(CI s,CI t,int ret=0)
    		{
    			while (BFS(s,t)) memcpy(cur,head,t+1<<2),ret+=DFS(s,t,INF); return ret;
    		}
    }NF;
    #undef to
    int main()
    {
    	RI i,j,k; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
    	scanf("%d%d%d%d",&l1[i],&r1[i],&l2[i],&r2[i]),
    	--l1[i],rstl[++numl]=l1[i],rstl[++numl]=l2[i],
    	--r1[i],rstr[++numr]=r1[i],rstr[++numr]=r2[i];
    	sort(rstl+1,rstl+numl+1); numl=unique(rstl+1,rstl+numl+1)-rstl-1;
    	sort(rstr+1,rstr+numr+1); numr=unique(rstr+1,rstr+numr+1)-rstr-1;
    	for (i=1;i<numl;++i) addedge(s,i,rstl[i+1]-rstl[i]);
    	for (t=numl+numr+1,i=1;i<numr;++i) addedge(numl+i,t,rstr[i+1]-rstr[i]);
    	for (i=1;i<=m;++i)
    	{
    		l1[i]=findl(l1[i]); r1[i]=findr(r1[i]);
    		l2[i]=findl(l2[i]); r2[i]=findr(r2[i]);
    		for (j=l1[i];j<l2[i];++j) for (k=r1[i];k<r2[i];++k)
    		addedge(j,numl+k,INF);
    	}
    	return printf("%d",NF.Dinic(s,t)),0;
    }
    

    F. GCD Groups 2

    讲道理我并不知道这题正解是什么,当时开个玩笑写个随机就过了

    考虑我们随机从未选取的数中选择一个数,如果这个数扔到第一个集合中可以使当前的(gcd)变小就扔进去

    然后剩下的数都放进第二个集合里,我们发现这样的正确性显然,因为加入的数越多(gcd)一定不会变大

    但是随机的正确率我并不会证,大致理解了一下就是要求一个集合内存在一个质因子能整除每个数(gcd)才不为(1),由于数的分布比较均匀因此质因子分布也很均匀

    所以我们直接随机做,然后卡时(0.45s)的时候退出输(NO)即可

    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005;
    int n,p[N],a[N],g1,g2,tp; bool cs[N];
    inline int gcd(CI n,CI m)
    {
    	return m?gcd(m,n%m):n;
    }
    int main()
    {
    	//freopen("F.in","r",stdin); freopen("F.out","w",stdout);
    	RI i; for (srand(20030909),scanf("%d",&n),i=1;i<=n;++i)
    	scanf("%d",&a[i]),p[i]=i; while (1.0*clock()/CLOCKS_PER_SEC<=0.45)
    	{
    		for (random_shuffle(p+1,p+n+1),g2=0,g1=a[p[1]],cs[p[1]]=1,i=2;i<=n;++i)
    		if (g1!=1&&(tp=gcd(g1,a[p[i]]))<g1) cs[p[i]]=1,g1=tp; else g2=gcd(g2,a[p[i]]);
    		if (g1==1&&g2==1)
    		{
    			for (puts("YES"),i=1;i<=n;++i) printf("%d ",cs[i]+1); exit(0);
    		}
    		for (i=1;i<=n;++i) cs[i]=0;
    	}
    	return puts("NO"),0;
    }
    

    Postscript

    讲道理这场CF难度确实不大,不过也算是有挺多思维好题的吧

    今后得多做做这样的比赛QWQ

  • 相关阅读:
    linux strace 命令详解
    Redis执行Lua脚本示例
    getconf
    rc.sysinit 解析
    Linux系统启动内幕
    syslinux 和 grub
    isolinux.cfg 文件是干什么的
    C++中构造函数调用构造函数
    static和extern的作用域--题目
    构造函数与析构函数不能被继承
  • 原文地址:https://www.cnblogs.com/cjjsb/p/11345223.html
Copyright © 2011-2022 走看看