Codeforces Round 1338 简要题解

47 篇文章 1 订阅
16 篇文章 0 订阅

A. Powered Addition

B. Edge Weight Assignment

C. Perfect Triples

找找规律容易发现答案跟四进制相关。
考虑将数字写成四进制,容易观察并归纳证明:所有匹配的 ( a , b , c ) (a,b,c) (a,b,c)位数都相同,并且位数相同,最高位为 1 1 1 2 2 2 3 3 3的数字分别会作为 a a a b b b c c c出现,互相匹配,且 a a a从小到大取遍所有最高位为 1 1 1的数,而后面每一位匹配关系是独立的,具体的,匹配关系是 ( 0 , 0 , 0 ) (0,0,0) (0,0,0) ( 1 , 2 , 3 ) (1,2,3) (1,2,3) ( 2 , 3 , 1 ) (2,3,1) (2,3,1) ( 3 , 1 , 2 ) (3,1,2) (3,1,2)。那么将 n n n写成四进制即可反过来得到 s n s_n sn了。
时间复杂度 O ( t log ⁡ n ) \mathcal O(t\log n) O(tlogn)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f

using namespace std;

typedef long long ll;

const int A[4]={0,1,2,3};
const int B[4]={0,2,3,1};
const int C[4]={0,3,1,2};

int main() {
  int cases;
  scanf("%d",&cases);
  for(;cases;cases--) {
  	ll n;
  	scanf("%lld",&n);
  	if (n<=3) {
  		printf("%lld\n",n);
  		continue;
	  }
	n-=4;
	int v=n%3;
	n/=3;
	ll d=1;
	int cnt=0;
	for(;;) {
		d*=4;
		cnt++; 
		if (d>n) break;
		n-=d;
	}
	int val[60],sz=0;
	memset(val,0,sizeof(val));
	while (n) {
		val[++sz]=n%4;
		n/=4;
	}
	ll s=0;
	if (!v) {
		s+=1;
		for(int i=cnt;i>0;i--) s=s*4LL+A[val[i]];
	}
	if (v==1) {
		s+=2;
		for(int i=cnt;i>0;i--) s=s*4LL+B[val[i]]; 
	}
	if (v==2) {
		s+=3;
		for(int i=cnt;i>0;i--) s=s*4LL+C[val[i]]; 
	}
	printf("%lld\n",s);
  }
  return 0;
}
/*
3
7
8
9 
*/ 

D. Nested Rubber Bands

考虑钦定一个点集 S S S,如何判定 S S S能否作为一个链出现。
首先注意到 S S S中不可能包含相邻的点。显然只用考虑 S S S中点构成的虚树上的点,对于虚树上某个非叶子节点 x x x,如果 x x x出现在 S S S中,那么容易证明合法必须满足以 x x x为根的话,至多两个子树中有 S S S中的点;如果 x x x没有出现在 S S S中,那么必须满足以 x x x为根的话,至多两个子树包含 S S S中的点且不是子树中仅有与 x x x相邻的点在 S S S中。
这样令 F [ i ] [ 0 / 1 ] [ 0 / 1 / 2 ] F[i][0/1][0/1/2] F[i][0/1][0/1/2]表示考虑点 i i i子树,点 i i i是否选择, i i i儿子子树中有几个包含 S S S中的点且不是子树中仅有与 i i i相邻的点在 S S S中,简单树形DP一下即可。
时间复杂度 O ( n ) \mathcal O(n) O(n)

#include <bits/stdc++.h>

using namespace std;

vector <int> e[100005];
int f[100005][2][3],ans;

void dfs(int x,int fa) {
  static int g[2][3];
  f[x][0][0]=0;
  f[x][1][0]=1;
  for(int i=0;i<e[x].size();i++)
    if (e[x][i]!=fa) {
    	int u=e[x][i];
    	dfs(u,x);
    	memcpy(g,f[x],sizeof(g));
        for(int j=0;j<3;j++) {
          if (f[x][0][j]!=-1) {
          	g[0][j]=max(g[0][j],f[x][0][j]+1);
          	for(int k=0;k<2&&j+1<3;k++) {
          		int t=max(f[u][0][k],f[u][1][k]);
          		if (t!=-1) g[0][j+1]=max(g[0][j+1],f[x][0][j]+t);
            }
		  }
		  if (f[x][1][j]!=-1) {
          	for(int k=0;k<2&&j+1<3;k++) {
          		int t=f[u][0][k];
          		if (t!=-1) g[1][j+1]=max(g[1][j+1],f[x][1][j]+t);
            }
		  }
	    }
	    memcpy(f[x],g,sizeof(f[x]));
	}
  for(int i=0;i<3;i++) {
  	ans=max(ans,f[x][0][i]);
  	ans=max(ans,f[x][1][i]);
  	if (fa) ans=max(ans,f[x][0][i]+1);
  }
}

int main() {
  memset(f,255,sizeof(f));
  int n;
  scanf("%d",&n);
  for(int i=1;i<n;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	e[x].push_back(y);
  	e[y].push_back(x);
  }
  dfs(1,0);
  printf("%d\n",ans);
  return 0;
}

E. JYPnation

给定的图是一个竞赛图。众所周知,竞赛图缩点后形成一条链,且取一个生成子图,若存在环必然存在三元环。显然根据题意,只可能有最后一个强连通分量大小 > 1 >1 >1。那么不断删去 0 0 0度点,只考虑一个非平凡强连通分量的情况。
考虑枚举点 x x x计算所有 d i s ( x , y ) dis(x,y) dis(x,y),那么可以发现 ∀ y \forall y y d i s ( x , y ) ≤ 3 dis(x,y)\leq 3 dis(x,y)3,证明是考虑若最短路长度 > 3 >3 >3,注意到最短路上不相邻的节点一定由后面连向前面,那么取出最短路上最后三个点及 x x x就非法了。
d i s ( x , i ) = 1 dis(x,i)=1 dis(x,i)=1显然当且仅当 x x x i i i有边,令 d i s ( x , i ) = 1 dis(x,i)=1 dis(x,i)=1的集合为 S S S d i s ( x , i ) > 1 dis(x,i)>1 dis(x,i)>1的集合为 T T T,显然 S S S T T T都非空。
考虑 T T T集合中一个点 y y y,使得 d i s ( x , y ) = 2 dis(x,y)=2 dis(x,y)=2,那么令 S S S中到 y y y有边的集合为 U U U,其他点集合为 V V V,显然 U U U非空。首先容易证明 U U U中无环,其次可以证明 U U U V V V连接的边都是由 V V V连向 U U U(否则若存在 p ∈ U p\in U pU q ∈ V q\in V qV,由 p p p连向 q q q,那么取出 x x x p p p y y y q q q就非法了),还能证明 V V V中也无环(否则必存在三元环,且全部连向至少一个 U U U中的点,显然非法)。
这样,可以发现 S S S内部形成一条链,且连向 y y y的点是这条链的一个后缀。事实上,甚至可以证明 T T T内部也形成一条链,且 d i s ( x , y ) = 3 dis(x,y)=3 dis(x,y)=3的点是这条链的一个后缀,并且从前往后考虑链上每个点, S S S到它的边数单调不升。
有了上面的引理就很好做了。考虑先求出 S S S内部最后一个点 m x mx mx,因为 S S S内部形成一条链,那么直接看两个点之间边的方向就可以判定在链上的相对位置关系,所以扫一遍即可。然后对于 T T T中每个点 y y y d i s ( x , y ) = 2 dis(x,y)=2 dis(x,y)=2当且仅当 m x mx mx y y y有边。
时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

bool e[8005][8005];
int ind[8005];

bool vis[8005];
char str[8005];

int main() {
  int n;
  scanf("%d",&n);
  for(int i=1;i<=n;i++) {
  	scanf("%s",str+1);
  	int r=0;
  	for(int j=1;j<=n;j+=4) {
  		r++;
  		int t=(isdigit(str[r]))?str[r]-'0':str[r]-'A'+10;
  		e[i][j]=((t>>3)&1);
  		e[i][j+1]=((t>>2)&1);
  		e[i][j+2]=((t>>1)&1);
  		e[i][j+3]=(t&1);
	  }
  }
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      if (e[i][j]) ind[j]++;
  ll ans=0;
  int sz=n;
  for(;;) {
  	int id=0;
  	for(int i=1;i<=n;i++)
  	  if (!vis[i]&&!ind[i]) {
  	  	  id=i;
  	  	  break;
		}
	if (!id) break;
	sz--;
	vis[id]=1;
	ans+=614LL*n*sz+sz;
	for(int i=1;i<=n;i++)
	  if (e[id][i]) ind[i]--;
  }
  for(int i=1;i<=n;i++)
    if (!vis[i]) {
    	int cnt=0,id=0;
    	for(int j=1;j<=n;j++)
    	  if (!vis[j]&&e[i][j]) {
    	  	cnt++;
    	  	if (!id||e[id][j]) id=j;
		  }
		ans+=cnt;
		for(int j=1;j<=n;j++)
		  if (!vis[j]&&i!=j&&!e[i][j]) ans+=((e[id][j])?2:3);
	}
  printf("%lld\n",ans);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值