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

    Codeforces Round #542 (Div. 1)

    似乎是一周前的比赛了QwQ,然而立过flag要每周写一场来着,就来补一补QwQ。

    A1/A2. Toy Train

    翻译

    (n)个点排成一圈,有(m)个货物,第(i)个货物要从(a_i)运到(b_i),在每个车站只能装一个货物,求从第(i)个车站出发,运送完所有货物的最短的时间。

    题解

    如果当前有多个元素,那么必须多走一圈回来拿。所以需要考虑的只有每个车站的最后一个被拿走的东西,显然这个东西就是目标位置离当前位置最近的那个货物。
    然后大力讨论一下就好了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 5050
    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 n,m,cnt[MAX],d[MAX];
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;++i)d[i]=n+1;
    	for(int i=1;i<=m;++i)
    	{
    		int l=read(),r=read();if(r<l)r+=n;
    		cnt[l]+=1;d[l]=min(d[l],r-l);
    	}
    	int mx=0;for(int i=1;i<=n;++i)mx=max(mx,cnt[i]);
    	for(int i=1;i<=n;++i)if(cnt[i]<max(1,mx-1))d[i]=0;
    	for(int i=1;i<=n;++i)
    	{
    		int ans=(mx-1)*n,mxx=0;
    		for(int j=1;j<=n;++j)
    			if(cnt[j]==mx)
    			{
    				int dis=i<=j?j-i:j-i+n;
    				dis+=d[j];mxx=max(mxx,dis);
    			}
    			else if(cnt[j]==mx-1)
    			{
    				int dis=j<i?i-j:i-j+n;
    				mxx=max(mxx,d[j]-dis);
    			}
    		printf("%d ",ans+mxx);
    	}
    	puts("");return 0;
    }
    

    B. Wrong Answer

    翻译

    给了你一个假贪心,你要构造一组数据把它卡掉,并且使得这组数据的结果和贪心的结果之差恰好为(k)

    题解

    不难发现可以让前面构造一段连续大于(0)的数,中间构造一段负数,然后再构造一段正数,贪心的结果就是两段正数的和乘上长度,答案是全局的和乘上长度,
    那么令前两个数是(1,-2),设一共有(n)个数,剩下的(n-2)个数的和是(S)
    那么有等式:((S-1)*n-S*(n-2)=k),解出来(2S=k+n)
    那么枚举所有的(n),判断一下能否放(n-2)个数使得他们的和为(S)即可。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int main()
    {
    	int k;scanf("%d",&k);
    	for(int n=1;n<=1998;++n)
    		if((k+n)%2==0)
    			if((k+n+2+1999999)/2000000<=n)
    			{
    				printf("%d
    ",n+2);
    				printf("1 -2 ");
    				int S=(k+n+2)/2;
    				for(int j=1;j<n;++j)
    					if(j==n-1&&S<=1000000)printf("%d ",S/2),S-=S/2;
    					else printf("1000000 "),S-=1e6;
    				printf("%d
    ",S);return 0;
    			}
    	puts("-1");
    	return 0;
    }
    

    C. Morse Code

    翻译

    长度为(1,2,3,4)的二进制串一共有(30)个,除了(0011,0101,1110,1111)之外,每一个都对应着一个字母。
    现在给定一个串(S),对于每一个(i),询问(S[1,i])中的所有子串中,本质不同的对应着一个英文字母串的拆分方案数。

    题解

    (f[l][r])表示(l,r)这段区间的拆分方案数,显然枚举最后一个字母是什么就好了。
    然后要求解的是本质不同的方案数,所以随便找个东西去去重就好了。
    可以写哈希,我写的(SAM)

    #include<iostream>
    #include<cstdio>
    #include<set>
    using namespace std;
    #define MOD 1000000007
    #define MAX 3030
    int m,S[MAX],f[MAX][MAX],top,ans;
    bool check(int l,int r)
    {
    	if(r-l+1<4)return true;
    	if(S[l]==0&&S[l+1]==0&&S[l+2]==1&&S[l+3]==1)return false;
    	if(S[l]==0&&S[l+1]==1&&S[l+2]==0&&S[l+3]==1)return false;
    	if(S[l]==1&&S[l+1]==1&&S[l+2]==1&&S[l+3]==0)return false;
    	if(S[l]==1&&S[l+1]==1&&S[l+2]==1&&S[l+3]==1)return false;
    	return true;
    }
    struct Node{int son[2],ff,len;set<int> vis;}t[MAX<<1];
    int tot=1,last=1;
    void extend(int c)
    {
    	int np=++tot,p=last;last=tot;
    	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[q].ff=t[np].ff=nq;
    			while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
    		}
    	}
    }
    int main()
    {
    	scanf("%d",&m);
    	for(int i=1;i<=m;++i)scanf("%d",&S[i]);
    	for(int i=m;i;--i)extend(S[i]);
    	for(int i=1;i<=m;++i)
    		for(int j=i;j>=max(1,i-3);--j)
    			if(check(j,i))
    			{
    				f[j][i]=(f[j][i]+1)%MOD;
    				for(int k=j-1;k;--k)
    					f[k][i]=(f[k][i]+f[k][j-1])%MOD;
    			}
    	for(int i=1;i<=m;++i)
    	{
    		for(int j=i,u=1;j;--j)
    		{
    			u=t[u].son[S[j]];
    			if(t[u].vis.count(i-j+1))continue;
    			t[u].vis.insert(i-j+1);
    			ans=(ans+f[j][i])%MOD;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    D. Isolation

    翻译

    把一个数组分成不相交的若干段,使得每一段中出现了恰好(1)次的数的个数至多是(k)个。
    求划分方案数。

    题解

    (f[i])表示前(i)个数的划分方案数。
    每次考虑一个(j),如果([j+1,i])是合法划分,那么(f[j] ightarrow f[i])
    考虑一个数值(v)什么时候会产生贡献,假设在(i)左侧第一次出现的位置是(x),第二次出现的位置是(y),那么当(jin [y+1,x])时,就会产生(1)的贡献。
    对于每个块维护一个区间加法标记以及每个权值的(f)的和,然后每次暴力维护区间加法就好啦。
    时间复杂度(O(nsqrt n))

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MOD 998244353
    #define MAX 100100
    void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
    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;
    }
    const int blk=800,py=MAX-10;
    int S[130][MAX*3],tag[130],Sum,val[MAX],bel[MAX];
    int n,k,a[MAX],lst[MAX],pre[MAX],f[MAX];
    void Modify(int l,int r,int w)
    {
    	for(int &i=l;i<=r&&bel[i-1]==bel[i];++i)
    	{
    		int K=k-tag[bel[i]];
    		add(S[bel[i]][val[i]+py],MOD-f[i-1]);
    		if(val[i]+tag[bel[i]]>=0&&val[i]<=K)add(Sum,MOD-f[i-1]);
    		val[i]+=w;add(S[bel[i]][val[i]+py],f[i-1]);
    		if(val[i]+tag[bel[i]]>=0&&val[i]<=K)add(Sum,f[i-1]);
    	}
    	for(int &i=r;i>=l&&bel[i+1]==bel[i];--i)
    	{
    		int K=k-tag[bel[i]];
    		add(S[bel[i]][val[i]+py],MOD-f[i-1]);
    		if(val[i]+tag[bel[i]]>=0&&val[i]<=K)add(Sum,MOD-f[i-1]);
    		val[i]+=w;add(S[bel[i]][val[i]+py],f[i-1]);
    		if(val[i]+tag[bel[i]]>=0&&val[i]<=K)add(Sum,f[i-1]);
    	}
    	if(l>r)return;
    	for(int i=bel[l];i<=bel[r];++i)
    	{
    		int R=k-tag[i],L=0-tag[i];
    		if(w==1)add(Sum,MOD-S[i][R+py]),add(Sum,S[i][L-1+py]);
    		else add(Sum,MOD-S[i][L+py]),add(Sum,S[i][R+1+py]);
    		tag[i]+=w;
    	}
    }
    int main()
    {
    	n=read();k=read();
    	for(int i=1;i<=n;++i)a[i]=read(),pre[i]=lst[a[i]],lst[a[i]]=i;
    	for(int i=0;i<=n+1;++i)bel[i]=(i+blk-1)/blk;
    	f[0]=Sum=S[1][py]=1;
    	for(int i=1;i<=n;++i)
    	{
    		int a=pre[i],b=pre[a];
    		Modify(a+1,i,1);if(a)Modify(b+1,a,-1);
    		f[i]=Sum;add(Sum,f[i]);
    		add(S[bel[i+1]][py],f[i]);
    	}
    	printf("%d
    ",f[n]);
    	return 0;
    }
    

    E. Legendary Tree

    翻译

    有一棵(n)个节点的树,每次可以询问两个点集(S,T)以及一个单点(v),交互库会返回有多少对((s,t))满足(sin S,tin T),且(s,t)的路径经过了(v)
    询问次数不超过(11111)

    题解

    首先随便钦定一个点作为根节点,假装是(1)
    然后询问(S={1},T=U-S-{v},v),其中(U)是全集,这样子就可以知道每棵子树的大小。
    我们按照子树大小从小往大处理,显然就是对于每一个点,在子树大小比他小的点中找它的儿子。如果找到了儿子可以直接把儿子在前面的点集中删去,那么每次把点集分成两半,如果左边有就往左边走,如果右边有就往右边走。
    不妨令每次恰好只找一个儿子,可以证明只需要(log)次询问就可以确定一个儿子。
    所以这样子的复杂度不会超过(nlogn)

    然后我自己还有一个奇怪的想法,不知道能不能优化:
    把所有点按照子树大小排序,显然子树最大的那个点(x)一定是(1)号点的一个儿子。
    然后对于个点询问(S={1},T={u},v=x),那么就可以知道一个点是不是(x)的儿子。
    然后把这棵子树分开就可以递归处理下去。然而这样子需要询问(O(n^2))次(菊花)。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<set>
    using namespace std;
    #define MAX 505
    int n,p[MAX],fa[MAX],size[MAX];
    set<int> S;
    bool cmp(int a,int b){return size[a]<size[b];}
    void Solve(int u,set<int>::iterator it,int tot)
    {
    	if(tot==1){fa[*it]=u;S.erase(it);return;}
    	int mid=tot>>1,x;set<int>::iterator tmp=it;
    	printf("%d
    ",mid);for(int i=1;i<=mid;++i,++it)printf("%d ",*it);puts("");
    	printf("1
    1
    %d
    ",u);fflush(stdout);
    	scanf("%d",&x);if(x)Solve(u,tmp,mid);tmp=it;
    	printf("%d
    ",tot-mid);for(int i=mid+1;i<=tot;++i,++it)printf("%d ",*it);puts("");
    	printf("1
    1
    %d
    ",u);fflush(stdout);
    	scanf("%d",&x);if(x)Solve(u,tmp,tot-mid);
    }
    int main()
    {
    	scanf("%d",&n);if(n==2){printf("ANSWER
    1 2
    ");return 0;}
    	for(int i=2;i<=n;++i)
    	{
    		printf("1
    %d
    %d
    ",1,n-2);
    		for(int j=2;j<=n;++j)if(i^j)printf("%d ",j);puts("");
    		printf("%d
    ",i);fflush(stdout);
    		scanf("%d",&size[i]);
    	}
    	for(int i=2;i<=n;++i)p[i-1]=i;
    	sort(&p[1],&p[n],cmp);
    	for(int i=1;i<n;++i)
    	{
    		int u=p[i];
    		if(!size[u]){S.insert(u);continue;}
    		Solve(u,S.begin(),S.size());
    		S.insert(u);
    	}
    	for(set<int>::iterator it=S.begin();it!=S.end();++it)fa[*it]=1;
    	puts("ANSWER");
    	for(int i=2;i<=n;++i)printf("%d %d
    ",i,fa[i]);
    	return 0;
    }
    
  • 相关阅读:
    遥控器油门摇杆电位器封装尺寸图
    Microhard P900 900MHz跳频电台核心模块
    航路点
    当电桥为恒流源时惠斯通电桥电压的计算方法
    曲轴位置传感器
    16种发动机动态工作原理图,神奇的帅呆了!
    ffmpeg mediacodec 硬解初探
    ffmpeg编码常见问题排查方法
    阿里云 访问控制RAM
    WannaCry勒索病毒处理指南
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10468011.html
Copyright © 2011-2022 走看看