Codeforces Round #148 (Div. 1) 总结

A. Not Wool Sequences

    给你两个数:n和m,要求用0~2^m-1这些数不重复的组成一个长度为n的序列,使得序列中不存在这样的子序列:

        (1 ≤ l ≤ r ≤ n) , 表示 x 异或 y

    现在问,总共有多少这样的序列

    好吧,这么一个水题硬是花了半个小时才AC

    思考方向:固定m,考虑长度为 n-1 和长度为 n 的序列之间的关系,就会发现一个神奇的规律,至于是什么,自己找吧,很有意思

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <ctime>
#include <cmath>

#include <functional>
#include <stdexcept>
#include <algorithm>
#include <iostream>
#include <utility>
#include <sstream>
#include <fstream>
#include <numeric>
#include <iomanip>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>

using namespace std;

typedef long long LL;
typedef double DB;
typedef unsigned long long ULL;
typedef unsigned int Uint;

const int INT_INF=0x3fffffff;
const int MOD=1000000009;
const int N=100001;
const int E=100001;

const DB EPS=1e-9;
const DB PI=3.14159265358979323846;

typedef vector<int> VI;
typedef vector<string> VS;
typedef vector<double> VD;
typedef pair<int, int> PII;

#define PB push_back
#define MP make_pair
#define MH make_heap
#define PH push

LL n,m;
LL ans;

LL power(LL n, LL p)
{
	if(p==0) return 0;
	LL odd=1;
	while(p>1)
	{
		if(p&1) odd=(odd*n)%MOD;
		n=(n*n)%MOD;
		p/=2;
	}
	return (odd*n)%MOD;
}

int main()
{
	scanf("%d%d", &n, &m);
	LL ans=1;
	LL temp=power(2, m);
	for(int i=1; i<=n; i++)
	{
		ans=(ans*(temp-i))%MOD;
	}
	printf("%I64d\n", ans);
    return 0;
}

B. Boring Partition

    给你一串数,让你给他们分成两份,现在定义一个函数:

        f(x,y):x+y,  x和y在同一个集合中

                  x+y+h ,x和y不在一个集合中

    现在要你找出一个分配方式,使得f_max-f_min 最小

    贪心,要么把最小的和其他的数放在一起,要么把最小的和其他的分开,当时看反复看了半小时,硬是没看懂题意

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <ctime>
#include <cmath>

#include <functional>
#include <stdexcept>
#include <algorithm>
#include <iostream>
#include <utility>
#include <sstream>
#include <fstream>
#include <numeric>
#include <iomanip>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>

using namespace std;

typedef long long LL;
typedef double DB;
typedef unsigned long long ULL;
typedef unsigned int Uint;

const int INT_INF=0x3fffffff;
const int MOD=1000000007;
const int N=100005;
const int E=100001;

const DB EPS=1e-9;
const DB PI=3.14159265358979323846;

typedef vector<int> VI;
typedef vector<string> VS;
typedef vector<double> VD;
typedef pair<int, int> PII;

#define PB push_back
#define MP make_pair
#define MH make_heap
#define PH push

struct data
{
	int x, id;
} a[N];
int n, h;
int belong[N];

bool cmp(data a, data b)
{
	return a.x<b.x;
}

int main()
{
	scanf("%d%d", &n, &h);
	for(int i=1; i<=n; i++)
	{
		scanf("%d", &a[i].x);
		a[i].id=i;
	}
	for(int i=1; i<=n; i++)
		belong[i]=1;
	sort(a+1, a+1+n, cmp);
	int ans1=a[n].x+a[n-1].x-a[1].x-a[2].x;
	int ans2=max(a[n].x+a[n-1].x, a[n].x+a[1].x+h)-min(a[2].x+a[3].x, a[1].x+a[2].x+h);
	if(ans2<ans1)
	{
		belong[a[1].id]=2;
		ans1=ans2;
	}
	printf("%d\n", ans1);
	for(int i=1; i<=n; i++)
	{
		printf("%d", belong[i]);
		if(i==n) printf("\n");
		else printf(" ");
	}
    return 0;
}

C. World Eater Brothers

    题意就是给你一棵树,初始时树中的边全是有向边,现在两个SB想占领这棵树,占领了一个节点,就把所有这个节点能够到达的节点全部占领了,如果需要占领更多的节点,就需要改变某些边的方向,使得能从占领的节点出发前往其他的节点,现在问你,这两个人选择哪两个节点用来占领这棵树,使得总共改变的边数量最少

    可以这样想,这两个人占领这棵树后一定是把这棵树分成了两个部分,每个部分分别是一棵树,现在就转变成了怎么在一棵树上选择一个节点占领,使得要占领这棵树总共翻转的边最少,这就两遍DFS就行了,暂且归类为树形DP吧,至于这两个人怎么分这棵树,直接暴力枚举,然后取代价最小的就行了,幸好当时这道题做出来了,不然就滚回Div2了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <ctime>
#include <cstdlib>
#include <stack>
#include <map>
#include <set>
#include <list>

#define MP make_pair
#define PB push_back
#define INT_INF 0x3fffffff
#define LL_INF 0x3fffffffffffffff
#define EPS 1e-12
#define MOD 1000000007
#define PI 3.14159265358979323846
#define N 200010
#define E 400010

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned int Uint;
typedef double DB;

struct Edge
{
    int st,en,val,next;
} edge[E];
int head[N] , tot;
int f[N];

void add_edge(int st,int en,int val)
{
    edge[tot].st=st;
    edge[tot].en=en;
    edge[tot].val=val;
    edge[tot].next=head[st];
    head[st]=tot++;
}

void dfs1(int u,int pre, int split_edge)
{
    f[u]=0;
    for(int e=head[u]; e!=-1; e=edge[e].next)
    {
        int v=edge[e].en;
        if(e==split_edge || e==split_edge+1) continue;
        if(v==pre) continue;
        f[u]+=edge[e].val;
        dfs1(v, u, split_edge);
        f[u]+=f[v];
    }
}

vector<int> res;

int ans;
void dfs2(int u,int pre,int now, int split_edge)
{
    if(now+f[u]<ans)
    {
        res.clear();
        ans=now+f[u];
        res.PB(u);
    }
    else if(now+f[u]==ans) res.PB(u);
    for(int e=head[u]; e!=-1; e=edge[e].next)
    {
        int v=edge[e].en;
        if(e==split_edge || e==split_edge+1) continue;
        if(v==pre) continue;
        int cnt=now+f[u]-f[v];
        if(edge[e].val==0) cnt++;
        if(edge[e].val==1) cnt--;
        dfs2(v,u,cnt,split_edge);
    }
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        tot=0;
        for(int i=1,a,b; i<n; i++)
        {
            scanf("%d%d",&a,&b);
            add_edge(a,b,0);
            add_edge(b,a,1);
        }
        int Min=INT_INF;
        for(int e=0; e<tot; e++)
        {
            if(edge[e].val==1) continue;
            int temp=0;

            dfs1(edge[e].st, -1, e);
            ans=INT_INF;
            res.clear();
            dfs2(edge[e].st, -1, 0, e);

            temp+=ans;

            dfs1(edge[e].en, -1, e);
            ans=INT_INF;
            res.clear();
            dfs2(edge[e].en, -1, 0, e);

            temp+=ans;

            if(temp<Min) Min=temp;
        }
		if(n==1) Min=0;
        printf("%d\n", Min);
    }
    return 0;
}

D和E没有想法。。。

总的来说,这次没能读懂题意是最大的失误,还有就是水题出的太慢了,人家A题直接秒了,而我还要花半个小时才能够搞出来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值