• 湖南集训day5


    难度:☆☆☆☆☆☆☆

    /*
    二分答案
    算斜率算截距巴拉巴拉很好推的公式
    貌似没这么麻烦我太弱了......
    唉不重要...
    */
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    
    #define N 100007
    
    using namespace std;
    int n,m,cnt;
    int X[N],Y[N];
    double k,x,y,b,dis2;
    struct segment
    {
        double k,b;
    }e[N<<1];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    double work(double x,double y)
    {
        return sqrt(double(x)*double(x)+double(y)*double(y));
    }
    
    int main()
    {
        freopen("geometry.in","r",stdin);
        freopen("geometry.out","w",stdout);
        n=read();
        for(int i=1;i<=n;i++) X[i]=read();
        for(int i=1;i<=n;i++) Y[i]=read();
        sort(X+1,X+n+1);sort(Y+1,Y+n+1);
        for(int i=1;i<=n;i++)
        {
            e[i].k=double(Y[i])/-double(X[i]);
            e[i].b=Y[i]; 
        }
        m=read();
        for(int i=1;i<=m;i++)
        {
            x=read();y=read();cnt=0;
            if(x==0)
            {
                int l=1,r=n,mid;
                while(l<=r)
                {
                    mid=l+r>>1;
                    if(Y[mid]>=y) cnt=mid,l=mid+1;
                    else r=mid-1;
                } 
            }
            double dis1=work(double(x),double(y));
            k=double(y)/double(x);
            int l=1,r=n,mid;
            while(l<=r)
            {
                mid=l+r>>1;
                if(work(double(e[mid].b/(k-e[mid].k)),k*double(e[mid].b/(k-e[mid].k)))<=dis1)
                  cnt=mid,l=mid+1;
                else r=mid-1;
            }
            printf("%d
    ",cnt);
        }
        fclose(stdin);fclose(stdout);
        return 0;
    }

    /*
    貌似数据水,贪心能贪70分...
    差分约束做法
    记录前缀和S[i],i>=8时首先要满足条件 s[i]-s[i-8]>=a[i]
    0<=s[i]-s[i-1]<=b[i]
    当i<8时有s[23]-s[16+i]+s[i]>=a[i]    
    因为第三个式子不满足差分约束形式,可以看出s[23]有单调性,可以二分…所以可以二分答案当常量处理 
    Spfa负环则无解
    还有一种网络流做法,表示很懵逼。
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    
    #define N 33
    
    using namespace std;
    int n,m,ans,cnt;
    int head[N<<1],dis[N],a[N],b[N];
    bool inq[N];
    struct edge{
        int u,v,dis,net;
    }e[N<<1];
    
    inline void add(int u,int v,int w)
    {
        e[++cnt].v=v;e[cnt].net=head[u];e[cnt].dis=w;head[u]=cnt;
    }
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    bool spfa()
    {
        queue<int>q;
        memset(dis,-0x7f7f7f,sizeof dis);
        memset(inq,0,sizeof inq);
        dis[0]=0;inq[0]=1;q.push(0);
        while(!q.empty())
        {
            int u=q.front();q.pop();inq[u]=0;
            if(dis[u]>1000) return false;
            for(int i=head[u];i;i=e[i].net)
            {
                int v=e[i].v;
                if(dis[v]<dis[u]+e[i].dis)
                {
                    dis[v]=dis[u]+e[i].dis;
                    if(!inq[v]) inq[v]=1,q.push(v);
                }
            }
        }return true;
    }
    
    bool solve(int ans)
    {
        memset(head,0,sizeof head);
        cnt=0;
        for(int i=9;i<=24;i++) add(i-8,i,a[i]);
        for(int i=1;i<=8;i++)  add(i+16,i,a[i]- ans);
        for(int i=1;i<=24;i++) add(i-1,i,0);
        for(int i=1;i<=24;i++) add(i,i-1,-b[i]);
        add(0,24,ans);add(24,0,-ans);
        return spfa();
    }
    
    int main()
    {
        freopen("cashier.in","r",stdin);
        freopen("cashier.out","w",stdout);
        n=read();
        while(n--)
        {
            for(int i=1;i<=24;i++) a[i]=read();
            for(int i=1;i<=24;i++) b[i]=read();
            ans=0;
            while(1)
            {
                if(++ans>1000)
                {
                    ans=-1;break;
                }
                if(solve(ans)) break;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }

    线段树

    部分分可以dp f[i][j]表示前i个数选了j个的答案

    f[i][j]=f[i-1][j]+f[i-1][j-1]*a[i] (i选不选)

    k=1时线段树区间求和区间修改,我只会这个....

    线段树区间合并操作。

    k比较小,所以线段树每个节点维护一个区间答案记为f[i]

    考虑一段区间i,左边取j个右边就取i-j个 答案是每个方案的左边乘右边的和。

    就是i左儿子f[j]和右边的f[i-j] 所以f[i]=Σ(j=0~i) lc f[j]*rc f[i-j]

    考虑取反操作,i是奇数就取反,偶数无影响(因为是相乘)

    考虑区间加, 开始f[i] a1*a2……an  后来是(a1+c)*(a2+c)……(an+c)

    考虑类似二项式定理,当上述a1~an  n个方案如果取了j个了,分别为al1,al2……alj

    那考虑最后答案,如果已经选了j个方案是(al1+c)(al2+c)……(alj+c)再还能选i-j个 最后答案是C(len-i,i-j)*f[j]*c^(i-j)

    复杂度 O(k^2*nlogn)

    只懂思路,代码....果断粘std

    #include <cstdio>
    #include <cstdlib>
    #define MOD 1000000007
    #define N 100005
    typedef long long LL;
    using namespace std;
    struct Node
    {
    	LL f[11];
    } node[N * 4];
    LL a[N], lazy1[N * 4];
    bool lazy2[N * 4];
    LL C[N][11];
    
    Node merge(Node lc, Node rc)
    {
    	Node o;
    	o.f[0] = 1;
    	for (int i = 1; i <= 10; i++)
    	{
    		o.f[i] = 0;
    		for (int j = 0; j <= i; j++)
    			o.f[i] = (o.f[i] + lc.f[j] * rc.f[i - j] % MOD) % MOD;
    	}
    	return o;
    }
    
    void build(int o, int l, int r)
    {
    	if (l == r)
    	{
    		for (int i = 0; i <= 10; i++) node[o].f[i] = 0;
    		node[o].f[0] = 1;
    		node[o].f[1] = (a[l] % MOD + MOD) % MOD;
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	build(o * 2, l, mid);
    	build(o * 2 + 1, mid + 1, r);
    	node[o] = merge(node[o * 2], node[o * 2 + 1]);
    	return ;
    }
    
    void update1(int o, int l, int r, int c)
    {
    	int len = r - l + 1;
    	LL ff[11];
    	for (int i = 0; i <= 10; i++) ff[i] = node[o].f[i];
    	for (int i = 1; i <= 10; i++)
    	{
    		node[o].f[i] = 0;
    		LL t = 1;
    		for (int j = 0; j <= i; j++)
    		{
    			LL tmp = ff[i - j] * C[len - (i - j)][j] % MOD * t % MOD;
    			node[o].f[i] = (node[o].f[i] + tmp) % MOD;
    			t = t * c % MOD;
    		}
    	}
    	return ;
    }
    
    void push_down(int o, int l, int r)
    {
    	int mid = (l + r) >> 1;
    	if (lazy1[o])
    	{
    		if (lazy2[o * 2])
    			lazy1[o * 2] = (lazy1[o * 2] + MOD - lazy1[o]) % MOD;
    		else
    			lazy1[o * 2] = (lazy1[o * 2] + lazy1[o]) % MOD;
    		if (lazy2[o * 2 + 1])
    			lazy1[o * 2 + 1] = (lazy1[o * 2 + 1] + MOD - lazy1[o]) % MOD;
    		else
    			lazy1[o * 2 + 1] = (lazy1[o * 2 + 1] + lazy1[o]) % MOD;
    		update1(o * 2, l, mid, lazy1[o]);
    		update1(o * 2 + 1, mid + 1, r, lazy1[o]);
    		lazy1[o] = 0;
    	}
    	if (lazy2[o])
    	{
    		lazy2[o * 2] ^= 1;
    		lazy2[o * 2 + 1] ^= 1;
    		for (int j = 1; j <= 10; j += 2)
    		{
    			node[o * 2].f[j] = MOD - node[o * 2].f[j];
    			node[o * 2 + 1].f[j] = MOD - node[o * 2 + 1].f[j];
    		}
    		lazy2[o] = 0;
    	}
    }
    
    void modify1(int o, int l, int r, int ll, int rr, int c)
    {
    	if (ll <= l && rr >= r)
    	{
    		if (lazy2[o]) lazy1[o] = (lazy1[o] + MOD - c) % MOD;
    		else lazy1[o] = (lazy1[o] + c) % MOD;
    		update1(o, l, r, c);
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	push_down(o, l, r);
    	if (ll <= mid) modify1(o * 2, l, mid, ll, rr, c);
    	if (rr > mid) modify1(o * 2 + 1, mid + 1, r, ll, rr, c);
    	node[o] = merge(node[o * 2], node[o * 2 + 1]);
    	return ;
    }
    
    void modify2(int o, int l, int r, int ll, int rr)
    {
    	if (ll <= l && rr >= r)
    	{
    		for (int i = 1; i <= 10; i += 2) node[o].f[i] = MOD - node[o].f[i];
    		lazy2[o] ^= 1;
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	push_down(o, l, r);
    	if (ll <= mid) modify2(o * 2, l, mid, ll, rr);
    	if (rr > mid) modify2(o * 2 + 1, mid + 1, r, ll, rr);
    	node[o] = merge(node[o * 2], node[o * 2 + 1]);
    	return ;
    }
    
    Node query(int o, int l, int r, int ll, int rr)
    {
    	if (ll <= l && rr >= r)
    		return node[o];
    	int mid = (l + r) >> 1;
    	push_down(o, l, r);
    	if (rr <= mid) return query(o * 2, l, mid, ll, rr);
    	if (ll > mid) return query(o * 2 + 1, mid + 1, r, ll, rr);
    	Node lc = query(o * 2, l, mid, ll, rr);
    	Node rc = query(o * 2 + 1, mid + 1, r, ll, rr);
    	return merge(lc, rc);
    }
    
    int main(int argc, char ** argv)
    {
    	freopen("game.in", "r", stdin);
    	freopen("game.out", "w", stdout);
    	int n, m;
    	scanf("%d %d", &n, &m);
    	C[0][0] = 1;
    	for (int i = 1; i <= n; i++)
    	{
    		C[i][0] = 1;
    		for (int j = 1; j <= 10; j++)
    			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
    	}
    	for (int i = 1; i <= n; i++)
    		scanf("%d", &a[i]);
    	build(1, 1, n);
    	for (int i = 1; i <= m; i++)
    	{
    
    		int l, r, opt;
    		scanf("%d%d%d",&opt, &l, &r);
    		if (opt == 1)
    		{
    			int c;
    			scanf("%d", &c);
    			c = (c % MOD + MOD) % MOD;
    			modify1(1, 1, n, l, r, c);
    		}
    		else if (opt == 2)
    		{
    			modify2(1, 1, n, l, r);
    		}
    		else
    		{
    			int k;
    			scanf("%d", &k);
    			Node o = query(1, 1, n, l, r);
    			printf("%d
    ", o.f[k] % MOD);
    		}
    	}
    	return 0;
    }
    

      

    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    新概念英语(第一册)Lesson 1
    第七篇、Python之模块/包
    解压序列
    eval函数
    python--magic module 文件类型识别
    MIME_type
    彻底删除git中的文件(包括历史提交记录)
    for循环与range()函数
    Linux 内核的 Makefile
    Python module ---- abc
  • 原文地址:https://www.cnblogs.com/L-Memory/p/7648032.html
Copyright © 2020-2023  润新知