8月11日算法日记

前言

        又是两场,感觉一般的内容已经基本可以解决了,欠缺一点稍微复杂点的数据结构的能力。今天把几道题说一下(其实早就搞定了

位运算的解题原则

        就和名字一样,位运算,大部分的题目只需按位进行分析即可,突出特点是两位之间一般没有联系。有联系的一般和其他知识相关联。

第7场

xay loves trees

题目大意:有两颗树,一颗是相容树,一颗是互斥树,输出满足这两颗树的最大集。

如果谈到最大集而非集合的数目,也就是说可以利用搜索的方式进行解决,也就是求得最优解的过程,是较为简单的。

本题关键在第二棵树,将一个点与其他点关联起来,然后利用第一颗树进行dfs就可以解决。

而第二点在于如何快速的收集这个结果。已知每个点最多有且只能有一个在集合中,也就是讲:如果最大值不为1,则非法。因此选用树状数组的方式进行询问(树上滑窗?

而问题在于,为什么不在更新时直接利用2进行检测呢?因为单次数据的插入是更新整片的过程,如果在更新时利用2进行直接检测,则一会破坏运算的连续性,二则在于更新时不好写(懒),最终写出的代码也是非常难看的,涉及到多个不连续更新过程。

因此,本题代码。

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 10;

int cnt, ans, vis[N], b[N], sz[N], num[N], dfn[N], n;
deque<int> q;
vector<int> edge[N], edge2[N];

int w[N];
struct Node
{
    int l, r, mx, add;
}tr[N * 4];

void pushup(int u) {
    tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}//mx为收集上来的最大值
//
void pushdown(int u)
{
    auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    if (root.add)
    {
        left.add += root.add, left.mx += root.add;
        right.add += root.add, right.mx += root.add;
        root.add = 0;
    }
}

void build(int u, int l, int r)
{
    if (l == r) tr[u] = {l, r, 0, 0};
    else
    {
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}
//更新范围内所有的结点
void modify(int u, int l, int r, int d)
{
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].mx += d;
        tr[u].add += d;
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, d);
        if (r > mid) modify(u << 1 | 1, l, r, d);
        pushup(u);
    }
}

//询问区间内最大值
//最开始问的是1,也就是最大的那个区间
int query(int u, int l, int r) {
    //在范围内
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].mx;

    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    int mx = 0;
    if (l <= mid) mx = max(mx, query(u << 1, l, r));
    if (r > mid) mx = max(mx, query(u << 1 | 1, l, r));
    return mx;
}
//对可行集的遍历
void dfs(int u) {
    for (int v : edge[u])
        if (!vis[v]) {
            vis[v] = 1;
            //更新联通集大小
            //拿到互斥集此点对应的边到互斥联通集,更新所有结点
            //看看这个家伙有没有亲戚,有亲戚就改一下,最好是孤儿
            //-1是自己,自己是这家伙爹,不能死了,所以要-1//好在是个DAG
            modify(1, num[v], num[v] + sz[v] - 1, 1);//1 1 n 1//u l r d
            //孤儿就是叶子结点或孤立结点
            if (query(1, 1, n) >= 2){//看看是不是孤儿
                int w = q.front();
                modify(1, num[w], num[w] + sz[w] - 1, -1);
                q.pop_front();
                q.push_back(v);
                dfs(v);
                modify(1, num[w], num[w] + sz[w] - 1, 1);//再搞回来,继续搜这个孤儿
                q.push_front(w);
            } else {//是孤儿,可以更新
                q.push_back(v);
                ans = max(ans, (int)q.size());//获取结果的最大非互斥集大小
                dfs(v);//可以进行下一个结点的检测
            }
            q.pop_back();
            //把刚刚更新的再删掉,避免-之类的
            modify(1, num[v], num[v] + sz[v] - 1, -1);
        }
}

void dfs1(int u) {
    dfn[++cnt] = u;//边对点
    num[u] = cnt;//点对边
    sz[u] = 1;//互斥集联通
    for (int v :edge2[u])//遍历edge2非可行集
        if (!vis[v]){
            vis[v] = 1;
            dfs1(v);
            sz[u] += sz[v];
        }
}

int main() {
    int tt;
    cin >> tt;
    while (tt--) {
        cin >> n;
        build(1, 1, n);
        cnt = 0;
        for (int i = 1; i <= n; i++) {
            edge[i].clear();
            edge2[i].clear();
        }

        for (int i = 2; i <= n; i++) {
            int u, v;
            cin >> u >> v;
            edge[u].push_back(v);
            edge[v].push_back(u);
        }

        for(int i = 2; i <= n; i++) {
            int u, v;
            cin >> u >> v;
            edge2[u].push_back(v);
            edge2[v].push_back(u);
        }

        memset(vis, 0, sizeof(vis));
        vis[1] = 1;
        dfs1(1);//求互斥集联通集大小-以1为根
        //for (int i = 1; i <= n; i++) cout << num[i] << '\n';
        memset(vis, 0, sizeof(vis));
        ans = 1;
        q.clear();
        q.push_back(1);
        modify(1, 1, n, 1);
        vis[1] = 1;
        dfs(1);

        cout << ans << '\n';
    }

    return 0;
}

Xray loves Floyd

题目大意:计算后固定k的正确个数。

解:先用dij解出直连的正确个数,然后考虑到如果有一个路径在最短路径上时也是可行的,因此进行dij还原,得到结果。

 

#include<bits/stdc++.h>
using namespace std;

const int maxn=2005,inf=0x3f3f3f3f;
int w[maxn][maxn],n;
vector<pair<int,int>> g[maxn];

void dij(int s,int *dis)
{
	priority_queue< pair<int,int>,vector<pair<int,int> >, greater<pair<int,int>> >q;
	q.push(make_pair(0,s));
	dis[s]=0;
	int x,y,k;
	while(!q.empty()){
		tie(k,x)=q.top(); q.pop();
		for(auto it:g[x]){
			y=it.first; k=it.second;
			if(dis[y]>dis[x]+k){
				dis[y]=dis[x]+k;
				q.push(make_pair(dis[y],y));
			}
		}
	}
}

vector<int>q[maxn];
int ok[maxn][maxn];

int main()
{
	int x,y,k,m;
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d%d",&x,&y,&k);
		g[x].push_back(make_pair(y,k));
	}
	memset(w,0x3f,sizeof(w));
	for(int i=1;i<=n;i++)dij(i,w[i]);
	for(int i=1;i<=n;i++)
		for(auto gg:g[i]){
			if(w[i][gg.first]==gg.second)q[i].push_back(gg.first),ok[i][gg.first]=1;
		} 
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(ok[i][j]||i==j||w[i][j]==inf){
				ans++; continue;
			}
			for(auto it:q[i])if(ok[it][j]&&w[i][j]==w[i][it]+w[it][j]){
				ans++; q[i].push_back(j); ok[i][j]=1; break;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

第8场脑筋急转弯比较多,不写日志了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值