zoukankan      html  css  js  c++  java
  • 部分CF Div2 E/F题解合集

    CF1391E

    一道挺套路的构造。

    先选出一棵(dfs)树,我们把根为链顶的重链弄下来,如果长度大于(lceil frac{n}{2} ceil)就输出。

    否则我们把重链的前某一半删掉,并且使得剩下的重链上的深度最小点的子树大小小于等于(lceil frac{n}{2} ceil)

    这样树就裂成了若干子树,每棵子树大小都小于总点数一半。因为(dfs)树上全是返祖边,所以这些子树中没有边相连。

    这样我们每次从两棵不同子树中拿两个点凑一对,容易发现这是合法的,每次从两个点数最大的子树拿两个点就能最大化点的对数了。用堆存一下即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000005;
    int n,m,k,t,u,v,head[N],Next[N*2],adj[N*2],son[N],siz[N],vis[N],tot,i,j,fa[N];
    vector<int> g[N];
    struct str{
    	int x;
    };
    bool operator <(str a,str b)
    {
    	return g[a.x].size()<g[b.x].size();
    }
    priority_queue<str> q;
    void Push(int u,int v)
    {
    	Next[++k]=head[u];
    	head[u]=k;
    	adj[k]=v;
    }
    void dfs(int i,int f)
    {
    	int j;
    	siz[i]=vis[i]=1;
    	son[i]=0;
    	fa[i]=f;
    	for(j=head[i];j;j=Next[j])
    		if(!vis[adj[j]])
    		{
    			dfs(adj[j],i);
    			siz[i]+=siz[adj[j]];
    			if(siz[adj[j]]>siz[son[i]])
    				son[i]=adj[j];
    		}
    }
    void dfs2(int i)
    {
    	int j;
    	vis[i]=1;
    	g[tot].push_back(i);
    	for(j=head[i];j;j=Next[j])
    		if(!vis[adj[j]])
    			dfs2(adj[j]);
    }
    int main()
    {
    	scanf("%d",&t);
    	while(t--)
    	{
    		scanf("%d %d",&n,&m);
    		for(i=1;i<=n;++i)
    			vis[i]=head[i]=0;
    		k=0;
    		for(i=1;i<=m;++i)
    		{
    			scanf("%d %d",&u,&v);
    			Push(u,v);
    			Push(v,u);
    		}
    		dfs(1,0);
    		for(i=1;i<=n;++i)
    			vis[i]=0;
    		int len=0;
    		for(i=1;i;i=son[i])
    			++len;
    		if(len>=(n+1)/2)
    		{
    			puts("PATH");
    			printf("%d
    ",len);
    			for(i=1;i;i=son[i])
    				printf("%d ",i);
    			printf("
    ");
    			continue;
    		}
    		tot=len=0;
    		for(i=1;i;i=son[i])
    		{
    			if(siz[i]<=(n-len)/2)
    			{
    				++tot;
    				g[tot].clear();
    				dfs2(i);
    				break;
    			}
    			++len;
    			vis[i]=1;
    			for(j=head[i];j;j=Next[j])
    				if(!vis[adj[j]]&&fa[adj[j]]==i&&adj[j]!=son[i])
    				{
    					++tot;
    					g[tot].clear();
    					dfs2(adj[j]);
    				}
    		}
    		for(i=1;i<=tot;++i)
    			q.push((str){i});
    		puts("PAIRING");
    		printf("%d
    ",((n+1)/2+1)/2);
    		int t=(n+1)/2;
    		while(q.size()>=2&&t>0)
    		{
    			str x=q.top();
    			q.pop();
    			str y=q.top();
    			q.pop();
    			printf("%d %d
    ",*g[x.x].rbegin(),*g[y.x].rbegin());
    			g[x.x].pop_back();
    			g[y.x].pop_back();
    			if(g[x.x].size())
    				q.push(x);
    			if(g[y.x].size())
    				q.push(y);
    			t-=2;
    		}
    		while(!q.empty())
    			q.pop();
    	}
    }
    

    CF1393E2

    我们可以设计一个简单的(DP),设(f_{ij})表示到第(i)个字符串为止,删去了第(j)个字符,且满足条件的方案数。

    这样就能过E1。

    对于E2我们要给每个字符串删掉一个字符后排序,并且对于相邻字符串,假设分别删掉了第(i)个和第(j)个字符(或不删),快速比较它们的大小。

    我们直接看第二个问题,显然第一个是第二个问题的弱化版。

    假设(i<j),我们的比较分为三部分:

    1.比较(a[1..i-1])(b[1..i-1])

    2.比较(a[i+1..j])(b[i..j-1])

    3.比较(a[j+1..|a|])(b[j+1..|b|])

    以上三者只要求出(lcp)就能找到两字符串不同的第一个位置。

    我们发现我们只要求出每一个(a)(b)从相同位置开始的后缀的(lcp),与(a)整体往右移动一格的后缀的(lcp),与(a)整体往左移动一格的后缀的(lcp)

    同样以第一种情况为例,我们有一个显然的性质,设(lcp_i)表示从(i)开始的后缀的(lcp),则(lcp_igeq lcp_{i-1}-1)

    这样我们暴力按顺序比较就是线性,然后我们就可以(O(1))比较两字符串删去一个字符的大小了。

    给每个字符串删掉一个字符后排序可以直接(sort),常数很小,但也可以线性。

    然后我们给排序后的相邻字符串弄个指针扫一下就行。

    复杂度(O(nlogn))(O(n))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000005;
    const int M=1000000007;
    int n,i,lp[N],j,a[N],la[N],ln,lcp[3][N],t;
    char c[N],lc[N];
    int dp[N],ldp[N];
    bool cmp(int u,int v)
    {
    	if(lp[min(u,v)]>=abs(v-u))
    		return u<v;
    	int fl=0;
    	if(u>v)
    	{
    		swap(u,v);
    		fl=1;
    	}
    	return (c[u+lp[u]+1]<c[u+lp[u]])^fl;
    }
    int main()
    {
    	scanf("%d",&t);
    	for(int m=1;m<=t;++m)
    	{
    		for(i=1;i<=n;++i)
    			c[i]=0;
    		scanf("%s",c+1);
    		n=strlen(c+1);
    		for(i=1;i<=n;)
    		{
    			for(j=i;c[i]==c[j];++j);
    			int p=j;
    			for(j=i;c[i]==c[j];++j)
    				lp[j]=p-j-1;
    			i=j;
    		}
    		for(i=1;i<=n;++i)
    			a[i]=i;
    		sort(a+1,a+1+n,cmp);
    		for(i=1;i<=n;++i)
    			if(a[i]+lp[a[i]]+1<=n&&c[a[i]+lp[a[i]]+1]>c[a[i]+lp[a[i]]])
    			{
    				for(j=n+1;j>i;--j)
    					a[j]=a[j-1];
    				break;
    			}
    		a[i]=0;
    		//for(i=1;i<=n+1;++i)
    		//	cout<<a[i]<<' ';
    		//cout<<endl;
    		if(m==1)
    		{
    			for(i=1;i<=n+1;++i)
    				dp[i]=i;
    		}
    		else
    		{
    			for(int f=-1;f<=1;++f)
    				for(i=1;i<=ln;++i)
    				{
    					lcp[f+1][i]=max(lcp[f+1][i-1]-1,0);
    					while(i+lcp[f+1][i]<=ln&&i+f+lcp[f+1][i]<=n&&lc[i+lcp[f+1][i]]==c[i+f+lcp[f+1][i]])
    						++lcp[f+1][i];
    				}
    			int l=1;
    			for(i=1;i<=n+1;++i)
    			{
    				while(l<=ln+1)
    				{
    					//cout<<'#'<<la[l]<<' '<<a[i]<<endl;
    					if(la[l]!=0&&a[i]!=0)
    					{
    						if(la[l]<a[i])
    						{
    							if(lcp[1][1]<la[l]-1)
    							{
    								if(lc[1+lcp[1][1]]>c[1+lcp[1][1]])
    									break;
    							}
    							else
    								if(lcp[0][la[l]+1]<a[i]-la[l])
    								{
    									if(lc[la[l]+1+lcp[0][la[l]+1]]>c[la[l]+lcp[0][la[l]+1]])
    										break;
    								}
    								else
    									if(lc[a[i]+1+lcp[1][a[i]+1]]>c[a[i]+1+lcp[1][a[i]+1]])
    										break;
    						}
    						if(la[l]==a[i])
    						{
    							if(lcp[1][1]<la[l]-1)
    							{
    								if(lc[1+lcp[1][1]]>c[1+lcp[1][1]])
    									break;
    							}
    							else
    							{
    								if(lc[a[i]+1+lcp[1][a[i]+1]]>c[a[i]+1+lcp[1][a[i]+1]])
    									break;
    							}
    						}
    						if(la[l]>a[i])
    						{
    							if(lcp[1][1]<a[i]-1)
    							{
    								if(lc[1+lcp[1][1]]>c[1+lcp[1][1]])
    									break;
    							}
    							else
    								if(lcp[2][a[i]]<la[l]-a[i])
    								{
    									if(lc[a[i]+lcp[2][a[i]]]>c[a[i]+1+lcp[2][a[i]]])
    										break;
    								}
    								else
    									if(lc[la[l]+1+lcp[1][la[l]+1]]>c[la[l]+1+lcp[1][la[l]+1]])
    										break;
    						}
    					}
    					if(la[l]!=0&&a[i]==0)
    					{
    						if(lcp[1][1]<la[l]-1)
    						{
    							if(lc[1+lcp[1][1]]>c[1+lcp[1][1]])
    								break;
    						}
    						else
    							if(lc[la[l]+1+lcp[0][la[l]+1]]>c[la[l]+lcp[0][la[l]+1]])
    								break;
    					}
    					if(la[l]==0&&a[i]!=0)
    					{
    						if(lcp[1][1]<a[i]-1)
    						{
    							if(lc[1+lcp[1][1]]>c[1+lcp[1][1]])
    								break;
    						}
    						else
    							if(lc[a[i]+lcp[2][a[i]]]>c[a[i]+1+lcp[2][a[i]]])
    								break;
    					}
    					if(la[l]==0&&a[i]==0)
    						if(lc[1+lcp[1][1]]>c[1+lcp[1][1]])
    							break;
    					++l;
    				}
    				dp[i]=ldp[l-1];
    				//cout<<l<<' ';
    			}
    			//cout<<endl;
    			for(i=1;i<=n+1;++i)
    				dp[i]=(dp[i-1]+dp[i])%M;
    		}
    		for(i=1;i<=ln;++i)
    			lc[i]=0;
    		for(i=1;i<=n;++i)
    			lc[i]=c[i];
    		for(i=1;i<=n+1;++i)
    		{
    			la[i]=a[i];
    			ldp[i]=dp[i];
    		}
    		ln=n;
    	}
    	cout<<dp[n+1]<<endl;
    }
    

    CF1379F1

    (n imes m)显然是能放的最大值。

    我们按(4 imes 4)的格子分组,显然每一组只能放左上或右下。

    我们发现对于任意一种放置方案,我们可以找到一条折线把这些(4 imes 4)的区域分开,使得左上角都是靠左上放的,右下角都是靠右下放的。

    每次ban掉一个位置就相当于钦定只能放左上/右下,显然一个只能放左上的块在只能放右下的块的左上方就是不行的。

    我们二分一个答案,然后直接判断,找出YES和NO的分界线即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200005;
    int n,m,q,x[N],y[N],i,l,r;
    struct str{
    	int x,y,c;
    }a[N];
    bool cmp(str a,str b)
    {
    	if(a.x!=b.x)
    		return a.x<b.x;
    	if(a.y!=b.y)
    		return a.y<b.y;
    	return a.c>b.c;
    }
    bool check(int m)
    {
    	int i,mn=1<<30;
    	for(i=1;i<=m;++i)
    		a[i]=(str){(x[i]+1)/2,(y[i]+1)/2,x[i]&1};
    	sort(a+1,a+1+m,cmp);
    	for(i=1;i<=m;++i)
    		if(a[i].c==1)
    			mn=min(mn,a[i].y);
    		else
    			if(a[i].y>=mn)
    				return false;
    	return true;
    }
    int main()
    {
    	scanf("%d %d %d",&n,&m,&q);
    	for(i=1;i<=q;++i)
    		scanf("%d %d",&x[i],&y[i]);
    	l=1,r=q+1;
    	while(l<r)
    	{
    		int mid=l+r>>1;
    		if(check(mid))
    			l=mid+1;
    		else
    			r=mid;
    	}
    	for(i=1;i<l;++i)
    		puts("YES");
    	for(i=l;i<=q;++i)
    		puts("NO");
    }
    

    对于F2其实就是套一个数据结构,线段树/平衡树就能处理

    CF1372F

    玄学题

    讲一个感觉很乱搞的做法

    我们设(f(l,r))表示在得知该区间众数的情况下求出(l~r)的范围内的答案,设众数出现次数为(cnt),则我们每(cnt)分一块,每一块分别询问,显然必然有一块能得到(lsim r)的众数,并且众数还是触碰到块的分界线,这样众数的出现位置就确定了。

    其余部分我们递归处理即可,这样看似一个颜色会被切很多份,但实际上在被切一刀就出现在首末了,首的颜色不可能被切,末的颜色被切一下直接发现某一整块都是同一颜色,因此实际操作是不多的

    合理分析+精细实现可能就是(4n)

    #include<bits/stdc++.h>
    using namespace std;
    int n,x,y,i,ans[200005];
    void color(int l,int r,int x)
    {
    	for(int i=l;i<=r;++i)
    		ans[i]=x;
    }
    void dfs(int l,int r,int x,int y)
    {
    	int j;
    	if(l>r)
    		return;
    	if(y==r-l+1)
    	{
    		color(l,r,x);
    		return;
    	}
    	int a[(r-l+1)/y+5],b[(r-l+1)/y+5],k=0;
    	for(j=l;j<=r;j+=y)
    	{
    		printf("? %d %d
    ",j,min(j+y-1,r));
    		fflush(stdout);
    		int p,q;
    		scanf("%d %d",&p,&q);
    		a[++k]=p,b[k]=q;
    	}
    	a[k+1]=b[k+1]=0;
    	int al,ar;
    	for(j=1;j<=k;++j)
    		if(a[j]==x)
    		{
    			if(a[j+1]==x||b[j]==y)
    			{
    				al=l+j*y-b[j],ar=l+j*y-b[j]+y-1;
    				color(al,ar,x);
    			}
    			else
    			if(j==k&&b[j]==(r-l)%y+1)
    			{
    				al=r-y+1,ar=r;
    				color(al,ar,x);
    			}
    			else
    			{
    				printf("? %d %d
    ",l+j*y-1,l+j*y-1);
    				fflush(stdout);
    				int p,q;
    				scanf("%d %d",&p,&q);
    				if(p==x)
    				{
    					al=l+j*y-b[j],ar=l+j*y-b[j]+y-1;
    					color(al,ar,x);
    				}
    				else
    				{
    					al=l+j*y-y+b[j]-y,ar=l+j*y-y+b[j]-1;
    					color(al,ar,x);
    				}
    			}
    			break;
    		}
    	for(j=1;j<=k;++j)
    	{
    		int ul=l+j*y-y,ur=min(l+j*y-1,r);
    		if(ul>=al&&ur<=ar)
    			continue;
    		if(ul<=al&&ur>=al)
    		{
    			if(a[j]==x)
    			{
    				printf("? %d %d
    ",ul,al-1);
    				fflush(stdout);
    				int p,q;
    				scanf("%d %d",&p,&q);
    				dfs(ul,al-1,p,q);
    			}
    			else
    				dfs(ul,al-1,a[j],b[j]);
    			continue;
    		}
    		if(ul<=ar&&ur>=ar)
    		{
    			if(a[j]==x)
    			{
    				printf("? %d %d
    ",ar+1,ur);
    				fflush(stdout);
    				int p,q;
    				scanf("%d %d",&p,&q);
    				dfs(ar+1,ur,p,q);
    			}
    			else
    				dfs(ar+1,ur,a[j],b[j]);
    			continue;
    		}
    		dfs(ul,ur,a[j],b[j]);
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	printf("? %d %d
    ",1,n);
    	fflush(stdout);
    	scanf("%d %d",&x,&y);
    	dfs(1,n,x,y);
    	printf("! ");
    	for(i=1;i<=n;++i)
    		printf("%d ",ans[i]);
    }
    

    CF1372E

    我们发现当两列之间有一种多种区间同时跨越它们时,显然这些区间全部选择同一列,比分散地选择两列要优。

    我们可以设计一个(DP),设(f_{ij})表示当(i-1)列与(j+1)列全是1时,(isim j)的最大价值

    我们枚举一个断点,把这一列中不跨越(i-1)(j+1)的区间全填1,然后分成了两个子问题,复杂度(O(n^3))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long double ld;
    const int N=1005;
    const ld pi=3.1415926535897932384626;
    int n,i,j,k,f[105][105],m,gl[105][105],gr[105][105],p,l,r;
    int dfs(int l,int r)
    {
    	if(l>r)
    		return 0;
    	if(f[l][r]!=-1)
    		return f[l][r];
    	int i,mx=0;
    	for(i=l;i<=r;++i)
    	{
    		int s=0;
    		for(j=1;j<=n;++j)
    			if(gl[j][i]>=l&&gr[j][i]<=r)
    				++s;
    		mx=max(mx,s*s+dfs(l,i-1)+dfs(i+1,r));
    	}
    	return f[l][r]=mx;
    }
    int main()
    {
    	scanf("%d %d",&n,&m);
    	memset(f,-1,sizeof(f));
    	for(i=1;i<=n;++i)
    	{
    		scanf("%d",&p);
    		for(j=1;j<=p;++j)
    		{
    			scanf("%d %d",&l,&r);
    			for(k=l;k<=r;++k)
    			{
    				gl[i][k]=l;
    				gr[i][k]=r;
    			}
    		}
    	}
    	memset(f,-1,sizeof(f));
    	cout<<dfs(1,m);
    }
    
  • 相关阅读:
    Asp.Net构架(Http请求处理流程)、(Http Handler 介绍)、(HttpModule 介绍)
    JQuery中的事件(三)
    关于asp.net mvc中的httpModules 与 httpHandler
    jQuery中的CSS(二)
    JQuery选择器(一)
    JavaScript中利用Ajax 实现客户端与服务器端通信(九)
    JavaScriptDom操作与高级应用(八)
    oracle(二)V$lock 视图中ID1 , ID2 列的含义
    关于static、内部类
    oracle(一)复习起航
  • 原文地址:https://www.cnblogs.com/invisible-eyes/p/13475097.html
Copyright © 2011-2022 走看看