zoukankan      html  css  js  c++  java
  • BSOJ 5603 -- 【SNOI2017】炸弹

    题好数据水系列,网上的十几行神仙解法A了原数据。

    这道题要用到线段数优化建图的知识。然而考试考到这道题时我还不会。

    我们设lx[i],rx[i]分别表示每个炸弹向左和向右最远能炸到哪个炸弹。很容易想到一个思路,就是每个炸弹都连边连向它能直接炸的所有炸弹,然后tarjan缩块后DAG图上DP。如果a到b有边,那么就能用b的lx[b],rx[b]更新lx[a],rx[a]。但是直接连边显然是不行的。

    我们仔细思考一下就会发现,一个炸弹能直接炸炸弹一定是一段连续的区间,这就给了我们想象的空间。我们就建一颗区间线段树,每个点的代表元就是对应的叶子节点。我们将所有节点(除了叶子节点)连向两个儿子节点。然后考虑一个炸弹i,假设它能直接引爆的区间是[l,r],那我们就用类似于线段树区间修改的方法将递归过程中遇到的包含于[l,r]的区间与i的代表元连边。然后就是tarjan的事了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    #include<vector>
    #include<ctime>
    #define ll long long
    #define N 2000005
    
    using namespace std;
    inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=1e9+7;
    struct tree {
    	int l,r;
    	int lx,rx;
    }tr[N];
    struct load {int to,next;}s[N*15];
    int h[N],cnt;
    void add(int i,int j) {
    	s[++cnt]=(load) {j,h[i]};h[i]=cnt;
    }
    int tot;
    void Link(int fr,int v,int l,int r) {
    	if(tr[v].l>r||tr[v].r<l) return ;
    	if(l<=tr[v].l&&tr[v].r<=r) {
    		if(fr!=v) add(fr,v);
    		return ;
    	}
    	Link(fr,v<<1,l,r);
    	Link(fr,v<<1|1,l,r);
    }
    int pos[N];
    void build(int v,int l,int r) {
    	tr[v].lx=tr[v].l=l;
    	tr[v].rx=tr[v].r=r;
    	tot=max(tot,v);
    	if(l==r) return pos[l]=v,void();
    	int mid=l+r>>1;
    	build(v<<1,l,mid),build(v<<1|1,mid+1,r);
    	add(v,v<<1),add(v,v<<1|1);
    }
    int n;
    ll x[N],r[N];
    vector<ll>p;
    int low[N],dfn[N],id;
    int st[N],bel[N],scc;
    int lx[N],rx[N];
    bool ins[N];
    void tarjan(int v) {
    	low[v]=dfn[v]=++id;
    	st[++st[0]]=v;
    	ins[v]=1;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(!dfn[to]) {
    			tarjan(to);
    			low[v]=min(low[v],low[to]);
    		} else if(ins[to]) low[v]=min(low[v],dfn[to]);
    	}
    	if(low[v]==dfn[v]) {
    		scc++;
    		while(1) {
    			int j=st[st[0]--];
    			ins[j]=0;
    			bel[j]=scc;
    			if(j==v) break;
    		}
    	}
    }
    vector<int>e[N];
    int d[N];
    bool vis[N];
    void dfs(int v) {
    	vis[v]=1;
    	for(int i=0,s=e[v].size();i<s;i++) {
    		int to=e[v][i];
    		if(!vis[to]) dfs(to);
    		lx[v]=min(lx[v],lx[to]);
    		rx[v]=max(rx[v],rx[to]);
    	}
    }
    ll ans;
    int main() {
    	int size=40<<20;//40M
    	__asm__ ("movq %0,%%rsp
    "::"r"((char*)malloc(size)+size));
    	n=Get();
    	build(1,1,n);
    	for(int i=1;i<=n;i++) {
    		x[i]=Get(),r[i]=Get();
    		p.push_back(x[i]);
    	}
    	for(int i=1;i<=n;i++) {
    		int Lx=lower_bound(p.begin(),p.end(),x[i]-r[i])-p.begin();
    		int Rx=upper_bound(p.begin(),p.end(),x[i]+r[i])-p.begin()-1;
    		lx[i]=Lx+1,rx[i]=Rx+1;
    		Link(pos[i],1,lx[i],rx[i]);
    	}
    	for(int i=1;i<=tot;i++) {
    		if(!dfn[i]) tarjan(i);
    	}
    	
    	for(int i=1;i<=scc;i++) lx[i]=n+1,rx[i]=0;
    	for(int i=1;i<=tot;i++) {
    		lx[bel[i]]=min(lx[bel[i]],tr[i].lx);
    		rx[bel[i]]=max(rx[bel[i]],tr[i].rx);
    		for(int j=h[i];j;j=s[j].next) {
    			int to=s[j].to;
    			if(bel[i]!=bel[to]) {
    				e[bel[i]].push_back(bel[to]);
    				d[bel[to]]++;
    			}
    		}
    	}
    	for(int i=1;i<=scc;i++)
    		if(!d[i]) dfs(i);
    	for(int i=1;i<=n;i++) {
    		ans=(ans+i*(rx[bel[pos[i]]]-lx[bel[pos[i]]]+1+mod)%mod)%mod;
    	}
    	cout<<ans;
    	exit(0);
    	return 0;
    }
    
  • 相关阅读:
    tensorflow之tf.meshgrid()
    Python中数据的保存和读取
    透视投影推导
    tensorflow之tf.slice()
    tensorflow的tf.train.Saver()模型保存与恢复
    偶数分割求平均值
    母牛的故事
    统计一行的不重复的单词字符个数
    N个顶点构成多边形的面积
    贪心法基本入门
  • 原文地址:https://www.cnblogs.com/hchhch233/p/9735819.html
Copyright © 2011-2022 走看看