zoukankan      html  css  js  c++  java
  • Codeforces Round #543 Div1题解(并不全)

    Codeforces Round #543 Div1题解

    Codeforces

    A. Diana and Liana

    给定一个长度为(m)的序列,你可以从中删去不超过(m-n*k)个元素,剩下的元素从左往右每(k)个一组,最后一组可以不满。给定你一个大小为(|S|)的可重集,要求你分出的组中至少有一组构成的可重集包含了给定的可重集。
    构造一种符合条件的删数方案。
    (n,m,k,|S|le 5*10^5)

    写了(1h)才过,感觉身败名裂。
    考虑枚举一个右端点(r),显然可以确定一个最大的(l)恰好包含了这个可重集,那么(check)一下这段([l,r])是否满足条件就好了。显然这个(l)随着(r)向右移动也是单调的。
    然后(WA)了半天,最后为了方便,强制(r-l+1ge k),这样子只需要在(l)之前删掉((l-1)\%k)个,在([l,r])之间删去(r-l+1-k)个,就很好写了。。。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 500500
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int m,K,n,S,tot,Del,a[MAX],b[MAX],c[MAX],d[MAX];
    int main()
    {
    	m=read();K=read();n=read();S=read();Del=m-K*n;
    	for(int i=1;i<=m;++i)a[i]=read();
    	for(int i=1;i<=S;++i)if(!b[read()]++)++tot;
    	for(int l=1,r=1;r<=m;++r)
    	{
    		++c[a[r]];tot-=c[a[r]]==b[a[r]];
    		while(!tot&&r-l+1>K&&l<=m&&c[a[l]]>b[a[l]])--c[a[l]],++l;
    		if(!tot&&r-l+1>=K)
    		{
    			int v=(l-1)%K+r-l+1-K;
    			if(v>Del)continue;
    			printf("%d
    ",v);if(!v)return 0;
    			for(int i=1;i<=(l-1)%K&&v;++i)printf("%d ",i),--v;
    			for(int i=l;i<=r&&v;++i)
    				if(d[a[i]]+1>b[a[i]])
    					printf("%d ",i),--v;
    				else d[a[i]]+=1;
    			return 0;
    		}
    	}
    	puts("-1");
    	return 0;
    }
    

    B. Once in a casino

    你有一个长度为(n)的,值域为(0-9)的元素序列,每次可以给相邻两个元素同时加一或者减一,但是仍然要在(0-9)的范围之内。回答能否把当前这些元素变成给定的某个元素序列,如果可以输出方案的前(10^5)步,否则输出(-1)
    (nle 10^5)

    首先不需要考虑在值域范围内的问题,从头到尾依次算一下,看看能否变过去就行了。
    现在构造方案,比如说你现在要给(x,x+1)位置加一,但是(x+1)位置是(9),那么你就递归处理,先让(x+1)位置减去一个(1),递归回来之后再给(x,x+1)位置(+1)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 100100
    int n,a[MAX],b[MAX],c[MAX];long long ans;char ch[MAX];
    void init(int *a){scanf("%s",ch+1);for(int i=1;i<=n;++i)a[i]=ch[i]-48;}
    void dfs(int x,int w)
    {
    	if(!ans)return;
    	if(0<=a[x+1]+w&&a[x+1]+w<=9){printf("%d %d
    ",x,w);a[x]+=w,a[x+1]+=w;--ans;return;}
    	dfs(x+1,-w);if(!ans)return;printf("%d %d
    ",x,w);a[x]+=w;a[x+1]+=w;--ans;
    }
    int main()
    {
    	scanf("%d",&n);init(a);init(b);
    	for(int i=1;i<=n;++i)c[i]=a[i];
    	for(int i=1;i<n;++i){int d=b[i]-c[i];c[i]+=d;c[i+1]+=d;ans+=abs(d);}
    	if(c[n]!=b[n]){puts("-1");return 0;}
    	cout<<ans<<endl;ans=min(ans,100000ll);
    	for(int i=1;i<n&&ans>0;++i)
    		while(a[i]!=b[i]&&ans>0)dfs(i,(b[i]-a[i])/abs(b[i]-a[i]));
    	return 0;
    }
    

    C. Compress String

    给定一个串,你可以把它进行划分,有两种划分方式:要么是一个字符成一组,代价是(a);要么是([l,r])划分一组,要求([l,r])([1,l-1])的一个子串,代价是(b)。求最小代价。
    (|S|le 5000)

    一边构建(SAM)一边(dp),就很简单。。。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 5050
    int n,a,b,f[MAX];char s[MAX];
    struct Node{int son[26],ff,len;}t[MAX<<1];
    int tot=1,last=1;
    void extend(int c)
    {
    	int p=last,np=++tot;last=np;t[np].len=t[p].len+1;
    	while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
    	if(!p)t[np].ff=1;
    	else
    	{
    		int q=t[p].son[c];
    		if(t[q].len==t[p].len+1)t[np].ff=q;
    		else
    		{
    			int nq=++tot;
    			t[nq]=t[q];t[nq].len=t[p].len+1;
    			t[np].ff=t[q].ff=nq;
    			while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
    		}
    	}
    	
    }
    int main()
    {
    	scanf("%d%d%d%s",&n,&a,&b,s+1);
    	for(int i=1;i<=n;++i)f[i]=1e9;
    	for(int i=1;i<=n;++i)
    	{
    		extend(s[i]-97);f[i]=min(f[i],f[i-1]+a);
    		for(int j=i+1,u=1;j<=n;++j)
    		{
    			int c=s[j]-97;
    			if(!t[u].son[c])break;
    			f[j]=min(f[j],f[i]+b);
    			u=t[u].son[c];
    		}
    	}
    	printf("%d
    ",f[n]);
    	return 0;
    }
    

    D. Power Tree

    给定一棵树,每个点你可以选或者不选,如果选就有一个代价,现在对于每个叶子节点,要求其到根节点的路径上选择的点的集合必须非空且两两不同,求最小代价。
    (nle 200000)

    如果有(k)个叶子节点,那么一定会被选择(k)个点。
    如果一个节点的儿子有多个叶子节点,那么至多只会有一个叶子节点不被选择。因此每个点的子树内要么选择了叶子节点个数个节点,要么是叶子个数减一。那么设(f[u][j=0/1])表示这个子树内选择了叶子节点个数(-j)个节点的最小代价。
    考虑如何转移:
    (f[u][1]=min_v{f[v][1]+sum_{w eq v}f[w][0]})
    (f[u][0]=min{sum f[v][1],f[u][0]+c[u]})
    对于求解每个点是否可能在最优方案中,则倒着(dp)再处理一遍就行了。

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define MAX 200200
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next;}e[MAX<<1];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    int n,c[MAX],S[MAX],top;ll f[MAX][2];
    bool leaf(int u){return !e[h[u]].next;}
    void dfs(int u,int ff)
    {
    	ll s=0;if(ff&&leaf(u)){f[u][1]=c[u];f[u][0]=0;return;}
    	f[u][0]=f[u][1]=1e18;
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)dfs(e[i].v,u),s+=f[e[i].v][1];
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)f[u][0]=min(f[u][0],s-f[e[i].v][1]+f[e[i].v][0]);
    	f[u][1]=min(s,f[u][0]+c[u]);
    }
    bool visf[MAX][2];
    void find(int u,int ff)
    {
    	if(visf[u][1])
    	{
    		if(f[u][1]==f[u][0]+c[u])S[++top]=u,visf[u][0]=true;
    		ll s=0;
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)s+=f[e[i].v][1];
    		if(f[u][1]==s)
    			for(int i=h[u];i;i=e[i].next)
    				if(e[i].v!=ff)visf[e[i].v][1]=true;
    	}
    	if(visf[u][0])
    	{
    		ll tmp=1e18;int tot=0;
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)tmp=min(tmp,f[e[i].v][0]-f[e[i].v][1]);
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)tot+=(tmp==f[e[i].v][0]-f[e[i].v][1]);
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)
    			{
    				if(tot>1||tmp<f[e[i].v][0]-f[e[i].v][1])visf[e[i].v][1]=true;
    				if(tmp==f[e[i].v][0]-f[e[i].v][1])visf[e[i].v][0]=true;
    			}
    	}
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)find(e[i].v,u);
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)c[i]=read();
    	for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
    	dfs(1,0);
    	printf("%I64d ",f[1][1]);visf[1][1]=true;
    	find(1,0);sort(&S[1],&S[top+1]);
    	printf("%d
    ",top);for(int i=1;i<=top;++i)printf("%d ",S[i]);puts("");
    	return 0;
    }
    

    QwQ

    剩下的题它们都鸽了。
    主要是前面把我写自闭了

  • 相关阅读:
    Java 8与Runtime.getRuntime().availableProcessors()
    nginx配置
    周末完成工作小结
    CentOS 8 安装MySQL 8.0
    centOS8网络获取不了
    IUAP平台新增菜单存储过程
    centOS8安装Docker
    Mybatis里用到的设计模式
    2020,回顾过往,展望未来
    使用 Apache SSI(Server Side Includes) 制作多语言版静态网页
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10530532.html
Copyright © 2011-2022 走看看