期末考试 Day2

7.7 期末考试Day2

今天的考试科目是:数学 && 生物

1 more hours later...

从来了就颓废到现在,除了把题面看了一遍啥也没干,还出去到了个垃圾(遛弯。

线段树分治

std

#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<ctime>
#include<stack>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define inf 0x3fffffff
#define ls p * 2
#define rs p * 2 + 1
#define fi first
#define se second
#define pb push_back
#define mp make_pair
using namespace std;

typedef long long ll;

typedef pair<int,int> pi;
typedef vector<int> vi;
typedef unsigned int ui;
const int N = 1e5 + 7, M = 2e5 + 7;
int n, m, k, u[M], v[M], f[N<<1], d[N<<1];

inline void rd(int &x){
	int res = 0, fl = 1;
	char c = getchar();
	while(!isdigit(c)){
		if(c == '-') fl = -1;
		c = getchar();
	}
	while(isdigit(c)){
		res = (res << 3) + (res << 1) + c - '0';
		c = getchar();
	}
	x = res * fl; 
}

struct T {
	int l, r, lss, rss;
	vi e;
} t[N<<2];
stack < pi > s;

void build(int p, int l, int r) {//常规建树
	t[p].l = l, t[p].r = r;
//	if (l == r) return;
    int mid = (l + r) >> 1;
//    printf("%d,%d\n",l,r);
	if(l != r)build(ls, l, mid); if(l != r)build(rs, mid + 1, r);
}

void ins(int p, int l, int r, int x) {
    if(l > t[p].r || r < t[p].l)    return;
//    cout << "intoins " << t[p].l << " " << t[p].r << " " << l << " " << r << " " << x << "\n";
	if (t[p].l >= l && t[p].r <= r){
        t[p].e.pb(x);
        return;//入栈
    }
    int md = (t[p].l + t[p].r) >> 1;
//    cout << t[ls].r << endl;
	if (l <= t[ls].r) ins(ls, l, r, x);
	if (r >= t[rs].l) ins(rs, l, r, x);
}

inline int get(int x) {
	while (x ^ f[x]) x = f[x];//不能路径压缩
	return x;
}

inline void merge(int x, int y) {
	if (x == y) return;
	if (d[x] > d[y]) swap(x, y);//启发式合并
	s.push(mp(x, d[x] == d[y])), f[x] = y, d[y] += d[x] == d[y];
}

void dfs(int p, int l, int r) {
	bool ok = 1;
	ui o = s.size();
	for (ui i = 0; i < t[p].e.size(); i++) {
		int x = t[p].e[i], u = get(::u[x]), v = get(::v[x]);
		if (u == v) {
			for (int j = l; j <= r; j++) printf("No\n");
			ok = 0;
			break;
		}
		merge(get(::u[x] + N), v), merge(get(::v[x] + N), u);//扩展域并查集操作
	}
	int mid = (t[p].l + t[p].r) / 2;
	if (ok) {
		if (l == r) printf("Yes\n");
		else dfs(ls, l, mid), dfs(rs, mid + 1, r);
	}
	while (s.size() > o) d[f[s.top().fi]] -= s.top().se, f[s.top().fi] = s.top().fi, s.pop();
}

int main() {
	rd(n), rd(m), rd(k), build(1, 1, k);
	for (int i = 1, l, r; i <= m; i++) {
		rd(u[i]), rd(v[i]), rd(l), rd(r);
		if (l ^ r) ins(1, l + 1, r, i);
	}
	for (int i = 1; i <= n; i++) f[i] = i, f[i+N] = i + N;
	dfs(1, 1, k);
	return 0;
}

看了hyl的代码后得出结论:线段树还是数组香,线段树还是不动态开点香。

P1999 高维正方体

space 0 1 2 3 4 5 6 7 8 9 10
1 2 1 0
2 4 4 1 0
3 8 12 6 1 0
4 16 30 24 8 1 0
5 32 80 80 40 10 1 0
6 64
7
8

观察得 \(f[i][0] = 2 ^ i\)

分析 i = 3 的情况:

一共8个点,每个点延伸出3条边,每条边被2个点共用,所以 \(f[3][1] = \frac{f[3][0] * 3} {2} = 12\)

一共12条边,每条边延伸出2个面,每个面由4条边构成,所以 \(f[3][2] = \frac{f[3][1] * 2} {4} = 6\)

一共6个面,每个面连出一个正方体,每个正方体由6个面构成,所以 \(f[3][3] = \frac{f[3][2] * 1}{6} = 1\)

推广出来通式:\(f[i][j] = \frac{f[i][j - 1] * (i + 1 - j)}{j * 2}\)

分析 i = 4,即4维超立方体的情况:

一共16个点,每个点延伸出4条边,每条边被2个点共用;

一共30条边,每条边延伸出3个面,每个面由4条边构成;

一共24个面,每个面延伸出2个正方体,每个正方体由6个面构成;

一共8个正方体,每个正方体延伸出1个四维超立方体,每个四维超立方体由8个正方体构成。

云云...

所以这题最终的解法就是O(b)从f[a][0]递推。

代码



int a, b;
ll f[100010], inv[200020];
ll ksm(ll base, int p){
    ll res = 1;
    while(p){
        if(p & 1){
            res *= base;
        }
        base *= base;
        res %= mod;
        base %= mod;
        p >>= 1;
    }
    return res;
}
void GetInv(){
    inv[1] = 1;
    for(int i = 2; i <= a * 2; ++i){
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
    return;
}
int main(){
    scanf("%d%d", &a, &b);
    if(a < b){
        printf("0\n");
        return 0;
    }
    f[0] = ksm(2, a);
    GetInv();
    for(int i = 1; i <= a; ++i){
        f[i] = f[i - 1] * (a - i + 1) % mod * inv[i * 2] % mod;
    }
    for(int i = 0; i <= a; ++i){
    }
    printf("%lld\n", f[b]);
	return 0;
}

P1064 [NOIP2006 提高组] 金明的预算方案

与传统01背包不同的是,每个主件不再是只有选/不选两种情况,由于还带有0/1/2个附件,所以每个主件一共有如下种情况:

没有附件:

不选主件/选主件(价格允许的话)

有一个附件:

不选主件/选主件(价格允许的话)/选主件和附件(价格允许选二者的话)

有两个附件:

不选主件/选主件(价格允许的话)/选主件和附件A(价格允许选二者的话)/选主件和附件B(价格允许选二者的话)/选主件和附件A和附件B(价格允许选二者的话)

这个题的特性是附件的数量少,只有0/1/2个,因此我们可以把附件涵盖在主件内处理而不用单独考虑附件的选择条件。

坑点:由于附件不单独处理,所以如果开二维数组进行DP每次沿用上一行的状态的话会调用到附件的空状态。所以应该直接开一维数组然后倒序枚举总钱数。

优化:由于物品价格都是10的倍数所以一开始就把n和v同时除以10最后总答案输出时再乘上10即可。

int n, m, q;
struct furniture{
    int v, p, ifmain;
    vi fujian;
}fur[66];
ll f[32010];
int main(){
    n = rd(); m = rd(); n /= 10;
    for(int i = 1; i <= m; ++i){
        fur[i].v = rd(); fur[i].v /= 10;
        fur[i].p = rd();
        q = rd();
        if(q != 0){
            fur[q].fujian.pb(i);
            fur[i].ifmain = 0;
        }
        else fur[i].ifmain = 1;
    }
    for(int i = 1; i <= m; ++i){
        if(!fur[i].ifmain)  continue;
        for(int j = n; j >= 1; --j){
            if(j < fur[i].v)    continue;
            int kind = fur[i].fujian.size();
            if(kind >= 0){
                f[j] = max(f[j], f[j - fur[i].v] + fur[i].v * fur[i].p);    
            }
            if(kind >= 1){
                if(j >= fur[i].v + fur[fur[i].fujian[0]].v)    
                    f[j] = max(f[j], f[j - fur[i].v - fur[fur[i].fujian[0]].v] + fur[i].v * fur[i].p + fur[fur[i].fujian[0]].v * fur[fur[i].fujian[0]].p);
            }
            if(kind >= 2){
                if(j >= fur[i].v + fur[fur[i].fujian[1]].v)
                    f[j] = max(f[j], f[j - fur[i].v - fur[fur[i].fujian[1]].v] + fur[i].v * fur[i].p + fur[fur[i].fujian[1]].v * fur[fur[i].fujian[1]].p);
                if(j >= fur[i].v + fur[fur[i].fujian[0]].v + fur[fur[i].fujian[1]].v)
                    f[j] = max(f[j], f[j - fur[i].v - fur[fur[i].fujian[0]].v - fur[fur[i].fujian[1]].v] + fur[i].v * fur[i].p + fur[fur[i].fujian[0]].v * fur[fur[i].fujian[0]].p + fur[fur[i].fujian[1]].v * fur[fur[i].fujian[1]].p);
            }
        }
    }
    printf("%lld\n", f[n] * 10);
	return 0;
}
posted @ 2021-07-07 21:48  咕咕坤  阅读(31)  评论(0编辑  收藏  举报