zoukankan      html  css  js  c++  java
  • [51nod2014] 小朋友的笑话

    问题描述

    小O是一个很萌很萌的女孩子。
    有一天小O叫了很多很多萌萌哒小朋友到家里来玩。
    由于太无聊了,她们开始讲笑话。
    总共有N个小朋友排成一排,编号1~N。
    在某个时刻,会有编号为xi的小朋友看到了笑话li,然后她会把这个笑话讲出来,与她距离不超过ki的小朋友都会听到这个笑话。
    当一个小朋友听到一个笑话时,如果她是第一次听得到这个笑话,那么她会觉得这个笑话非常好笑,笑的停不下来。如果她听到之前就是在笑的她会继续笑。
    如果她之前听过这个笑话,那么她会觉得这个笑话非常无聊,她会立即停止笑。

    现在小O想知道,在某些时刻一段区间内有多少小朋友在笑。

    输入格式

    第一行两个整数N,M,表示小朋友的数量和事件数量,(1<=N,M<=100000)
    接下来M行
    第一行为一个正整数op(1<=op<=2),表示事件类型。
    如果op=1,接下来三个整数xi li ki,表示该时间点小朋友xi(1<=xi<=N)讲了一个笑话li(1<=li<=100000),传播距离为ki(0<=ki<=N)。
    如果op=2,接下来两个整数l r,表示该时间点小O想知道区间[l,r]中有多少小朋友在笑(1<=l<=r<=N)

    输出格式

    对每个询问操作,输出答案

    样例输入

    10 14
    1 3 11 0
    2 3 3
    1 3 11 2
    2 1 5
    1 5 12 1
    2 4 6
    1 8 13 2
    2 6 10
    1 7 11 2
    2 5 9
    1 10 12 1
    2 9 10
    1 9 12 0
    2 1 10

    样例输出

    1
    4
    3
    5
    4
    2
    7

    解析

    最关键的一点是如何处理某个笑话在某个点是否重复出现过。

    单独考虑某一个笑话,问题就转化为区间覆盖,求哪些位置是第一次被覆盖。考虑用set维护所有区间,第一关键字为左端点。对于当前要插入的区间[l,r],我们可以利用 lower_bound很方便的求出所有与这个区间有交集的区间。我们先把[l,r]都标记为在笑,然后依次扫描刚才找到的有交集的区间,把交集部分标记为没在笑。最后再向set中插入这个区间。这样的修改和查询都可以用线段树解决。

    但是这样的时间复杂度不能保证。所以,每次我们都要把上面扫描到的区间以及当前区间都合并成一个区间。这样,每个区间都只会插入、修改、合并一次。这样就可以通过了。

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define int long long
    #define N 202
    using namespace std;
    const int mod=1000000007;
    struct card{
    	int a,g,p,id;
    }a[N*N];
    int n,m[N],v[N],cnt,i,j,f[N],ans[N],p[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    int poww(int a,int b)
    {
    	int ans=1,base=a;
    	while(b){
    		if(b&1) ans=ans*base%mod;
    		base=base*base%mod;
    		b>>=1;
    	}
    	return ans;
    }
    int my_comp(const card &x,const card &y)
    {
    	return x.a>y.a;
    }
    signed main()
    {
    	n=read();
    	for(i=1;i<=n;i++){
    		m[i]=read();
    		int inv=0;
    		for(j=1;j<=m[i];j++){
    			cnt++;
    			a[cnt].a=read(),a[cnt].g=read(),a[cnt].p=read();
    			a[cnt].id=i;
    			a[cnt].g=(100-a[cnt].g)*poww(100,mod-2)%mod;
    			inv=(inv+a[cnt].p)%mod;
    		}
    		inv=poww(inv,mod-2);
    		for(j=1;j<=m[i];j++) a[cnt-j+1].p=a[cnt-j+1].p*inv%mod;
    	}
    	for(i=1;i<=n;i++) v[i]=read();
    	sort(a+1,a+cnt+1,my_comp);
    	f[1]=1;
    	for(i=1;i<=cnt;i++){
    		if(a[i-1].id!=a[i].id){
    			int inv=poww(1-p[a[i].id]+mod,mod-2);
    			f[1]=f[1]*inv%mod;
    			for(j=2;j<=n;j++) f[j]=(f[j]-f[j-1]*p[a[i].id]%mod+mod)%mod*inv%mod;
    			for(j=n;j>=1;j--) f[j]=(f[j]*(1-p[a[i-1].id]+mod)%mod+f[j-1]*p[a[i-1].id]%mod)%mod;
    		}
    		for(j=1;j<=n;j++) ans[a[i].id]=(ans[a[i].id]+f[j]*v[j]%mod*a[i].p%mod*a[i].g%mod)%mod;
    		p[a[i].id]=(p[a[i].id]+a[i].p)%mod;
    	}
    	for(i=1;i<=n;i++) printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    while(~scanf(..))为什么可以这样写
    【 HDU3294 】Girls' research (Manacher)
    【 HDU2966 】In case of failure(KD-Tree)
    【 HDU 1538 】A Puzzle for Pirates (海盗博弈论)
    【 HDU 2177 】取(2堆)石子游戏 (威佐夫博弈)
    【 HDU 4936 】Rainbow Island (hash + 高斯消元)
    【 HDU1081 】 To The Max (最大子矩阵和)
    Partition Numbers的计算
    【 HDU
    【 Gym 101116K 】Mixing Bowls(dfs)
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12616696.html
Copyright © 2011-2022 走看看