Codeforces Round #633 (Div. 2) D(结论题) E(规律+构造)

题目链接

前三题水题就不写了(水题做了这么久,被自己菜醒)

D. Edge Weight Assignment

题意:给你一颗树,要你为每条边进行赋值,条件要符合每个叶子节点之间的边权异或得为0,现在求最小和最大得边权不同数得个数

做法:

这种题一看就是找结论的,多列几个树推一推就可以了。

结论:最大好构造,每条边 权都不同即可。因为边权是无限大的,一定存在某种合法方案使得边权异或为0

如果两个叶子节点之间得距离等于2,那么答案少一

dfs构造 有多个叶子节点的点 减去就可以了:

5-2==3

那么最小怎么构造呢?

最小的话只有两种情况,要么是1,要么是3

当所有叶子节点距离为偶数(边权为1)时 所有的边权可以相等。答案就是1

当存在一个叶子节点距离答案是3 

至于怎么找存在一个叶子之间的距离为奇数,我是用了换根dp,方法很多

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
int dp[N][2],n;
vector<int>G[N];
int mi,mx,du[N],root;
void dfs1(int u,int fa)
{
    if(du[u]==1){
        dp[u][0]++;
        return ;
    }
    for(int v:G[u])
    {
        if(v==fa) continue;
        dfs1(v,u);
        dp[u][1]+=dp[v][0];
        dp[u][0]+=dp[v][1];
    }
}
void dfs2(int u,int fa)
{
    if(du[u]==1&&dp[u][1]) {
        mi=3;
        //printf("u:%d\n",u);
    }
    if(mi!=1) return ;
    for(int v:G[u])
    {
        if(v==fa) continue;
        int t0=dp[u][0];
        int t1=dp[u][1];
        dp[u][0]-=dp[v][1];
        dp[u][1]-=dp[v][0];
        dp[v][0]+=dp[u][1];
        dp[v][1]+=dp[u][0];
        dfs2(v,u);
        dp[u][0]=t0;
        dp[u][1]=t1;
    }
}
int dfs3(int u,int fa)
{
    int flag=0;
    for(int v:G[u])
    {
        if(v==fa) continue;
        flag+=dfs3(v,u);
    }
    if(flag>=2) mx-=flag-1;
    if(du[u]==1) return 1;
    return 0;
}
int main()
{
	cin>>n;
	rep(i,2,n)
	{
	    int u,v;
	    scanf("%d%d",&u,&v);
	    G[u].push_back(v);
	    G[v].push_back(u);
	    du[u]++;
	    du[v]++;
	}
	rep(i,1,n) if(du[i]>=2) root=i;

	dfs1(root,-1);
	mi=1;
	dfs2(root,-1);

	mx=n-1;
	dfs3(root,-1);
	printf("%d %d\n",mi,mx);
}

E. Perfect Triples

题意:要你从小到大构造三元组 并且每个数是唯一的 且  a^b^c=0  依次连接序列s的后面,输入n  求第n个数是什么

做法:听群友说异或和  和  四进制 是好兄弟。按照题意打表:

打表程序:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
int vis[N];
void cal(int x)
{
    stack<int>sta;
    while(x)
    {
        int d=x%4;
        sta.push(d);
        x=x/4;
    }
    while(sta.size()) {
        printf("%d",sta.top());
        sta.pop();
    }
    printf(" ");
}
int main()
{
	rep(i,1,200)
	rep(j,1,200)
	rep(k,1,200){
        if(vis[i]||vis[j]||vis[k]) continue;
        if((i^j^k)==0){
             //cal(i),cal(j),cal(k);
             //puts("");
             printf("%d %d %d\n",i,j,k);
             vis[i]=1,vis[j]=1,vis[k]=1;
        }

	}
}


好像没什么规律啊

改成四进制看看:

按照4^次方 行每段分下段:

1 2 3

10 20 30
11 22 33
12 23 31
13 21 32

100 200 300
101 202 303
102 203 301
103 201 302
110 220 330
111 222 333
112 223 331
113 221 332
120 230 310
121 232 313
122 233 311
123 231 312
130 210 320
131 212 323
132 213 321
133 211 322



1000 2000 3000
1001 2002 3003
1002 2003 3001
1003 2001 3002
1010 2030 3020
1020 2031 3011
1021 2032 3013
1023 2033 3010
1030 2022 3012

隐隐约约发现一个规律,每个段内 三元组  第一个数的每一位都是0 1 2 3变化,并且0 1 2 3 连续的个数跟当前4

进制位数有关。

三元组的第二个数是0 2 3 1变化。

三元组第三个数是0 3 1 2的变化。

规律有了,但是构造起来好像很麻烦耶

说起来也麻烦,直接研究代码:

代码参考来自:代码来源

先求出来当前n处在三元组的某一段中 某一行p  以及当前段第一行 第一个元素 的数字s:

ll p=(n-1)/3,s=1,all=0;
while(p>=s)
{
    p-=s;
    s<<=2;
}

根据n%3 求得是在三元组的第几个数字。

总结:很妙的一个规律以及很妙的构造方法吧。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int _;cin>>_;while(_--)
    {
        ll n;
        cin>>n;
		ll p=(n-1)/3,s=1,all=0;
		//当前n处在三元组 某一段中 某一行p  当前段第一行 第一个元素 的数字s:
        while(p>=s)
        {
            p-=s;
			s<<=2;
        }
        
        //p行对应的进制变化分别是0,1,2,3     0 2 3 1      0 3 2 1
        if(n%3==1) printf("%lld\n",s+p);//第一个数直接加
        else if(n%3==2)//第二个
        {
            ll ans=s<<1,c=1;//每一段的第一行是特殊的 x 2*x 3*x
            while(p)
            {
            	//四进制的最低位
                ll x=p%4;
                if(x==1) ans+=c*2;
                else if(x==2) ans+=c*3;
                else if(x==3) ans+=c;
                p>>=2,c<<=2;
            }
            printf("%lld\n",ans);
        }
        else
        {
            ll ans=s*3,c=1;
            while(p)
            {
                ll x=p%4;
                if(x==1) ans+=c*3;
                else if(x==2) ans+=c*1;
                else if(x==3) ans+=c*2;
                p>>=2,c<<=2;
            }
            printf("%lld\n",ans);
        }

    }
    return 0;
}
发布了519 篇原创文章 · 获赞 69 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/105506582