• EZ 2018 07 06 NOIP模拟赛


    又是慈溪那边给的题目,这次终于没有像上次那样尴尬了,

    T1拿到了较高的暴力分,T2没写炸,然后T3写了一个优雅的暴力就203pts,Rank3了。

    听说其它学校的分数普遍100+,那我们学校还不是强到飞起。

    装备(equipment.pas/cpp/c)

    这题目出的真心长,让我联想到我们语文老师说的:

    以后教育改革可能不只是语文,其他学科的考试题目字数都要显著增加了,到时后数学大题你看都看不完。

    原来连OI的题目都有这种趋势。

    我们精简题意后发现:给你两段序列,求两两乘积的第(k)大值。

    然后瞄一眼数据范围,然后我就想到了一种经典的的解决方法。

    先排序,然后把所有的(a_icdot b_1)的值都扔到大根堆里,然后每次取出最大的值之后就把(b_i)的下标变成(i+1)再乘起来扔回去即可。

    复杂度是(O(qcdot k log k))的,肯定会炸。

    这里给出堆的代码(至于又开了小根堆是为了在(k)((b-a+1)(r-l+1)-k+1)中取一个较小的)

    53ptsCODE

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int N=255,M=100005;
    int n,m,a[N],b[M],c[N],d[M],q,opt,L,R,l,r,k;
    struct Small
    {
    	int x,y,s;
    	bool operator <(const Small a) const { return a.s<s; }
    };
    struct Big
    {
    	int x,y,s;
    	bool operator <(const Big a) const { return a.s>s; }
    };
    priority_queue <Small> small;
    priority_queue <Big> big;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; while (!isdigit(ch=tc()));
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline void copy(void)
    {
    	register int i;
    	for (i=l;i<=r;++i)
    	c[i-l+1]=a[i];
    	for (i=L;i<=R;++i)
    	d[i-L+1]=b[i];
    }
    inline bool small_cmp(int a,int b)
    {
    	return a<b;
    }
    inline bool big_cmp(int a,int b)
    {
    	return a>b;
    }
    inline void Big_solve(int k)
    {
    	sort(c+1,c+r-l+2,big_cmp); sort(d+1,d+R-L+2,big_cmp);
    	while (!big.empty()) big.pop();
    	for (register int i=1;i<=r-l+1;++i)
    	big.push((Big){i,1,c[i]*d[1]});
    	for (;;)
    	{
    		Big now=big.top(); big.pop();
    		if (!(--k)) { write(now.s); putchar('
    '); return; }
    		if (now.y^(R-L+1)) big.push((Big){now.x,now.y+1,c[now.x]*d[now.y+1]});
    	}
    }
    inline void Small_solve(int k)
    {
    	sort(c+1,c+r-l+2,small_cmp); sort(d+1,d+R-L+2,small_cmp);
    	while (!small.empty()) small.pop();
    	for (register int i=1;i<=r-l+1;++i)
    	small.push((Small){i,1,c[i]*d[1]});
    	for (;;)
    	{
    		Small now=small.top(); small.pop();
    		if (!(--k)) { write(now.s); putchar('
    '); return; }
    		if (now.y^(R-L+1)) small.push((Small){now.x,now.y+1,c[now.x]*d[now.y+1]});
    	}
    }
    int main()
    {
    	freopen("equipment.in","r",stdin); freopen("equipment.out","w",stdout);
    	register int i; read(n); read(m);
    	for (i=1;i<=n;++i) read(a[i]);
    	for (i=1;i<=m;++i) read(b[i]); read(q);
    	while (q--)
    	{
    		read(opt); read(l); read(r); read(L);
    		if (opt)
    		{
    			read(R); read(k); int tot=(r-l+1)*(R-L+1); copy();
    			if (k<tot-k+1) Big_solve(k); else Small_solve(tot-k+1);
    		} else
    		{
    			if (l) b[r]=L; else a[r]=L;
    		}
    	}
    	return 0;
    }
    

    其实这是一个经典的模板(我怎么没听说过),我们首先二分答案(x),然后对于其中一个数组排序。

    之后对于另一个数组中的所有数只需要再次二分统计个数即可。

    主要还是一个套路题。

    CODE

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    const int N=255,M=100005;
    int n,m,a[N],b[M],c[M],q,opt,L,R,l,r,k;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; while (!isdigit(ch=tc()));
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline bool cmp(int a,int b)
    {
    	return a>b;
    }
    inline void copy(void)
    {
    	register int i;
    	for (i=L;i<=R;++i)
    	c[i-L+1]=b[i]; 
    	sort(c+1,c+R-L+2,cmp);
    }
    inline int check(int x)
    {
    	int rk=0;
    	for (register int i=l;i<=r;++i)
    	{
    		int l_=1,r_=R-L+1;
    		while (l_<=r_)
    		{
    			int mid=l_+r_>>1;
    			if (c[mid]*a[i]<x) r_=mid-1; else l_=mid+1;
    		}
    		rk+=r_;
    	}
    	return rk;
    }
    inline void solve(void)
    {
    	int l=1,r=2e9;
    	while (l<=r)
    	{
    		int mid=(r-l>>1)+l;
    		if (check(mid)<k) r=mid-1; else l=mid+1;
    	}
    	write(r); putchar('
    ');
    }
    int main()
    {
    	freopen("equipment.in","r",stdin); freopen("equipment.out","w",stdout);
    	register int i; read(n); read(m);
    	for (i=1;i<=n;++i) read(a[i]);
    	for (i=1;i<=m;++i) read(b[i]); read(q);
    	while (q--)
    	{
    		read(opt); read(l); read(r); read(L);
    		if (opt) read(R),read(k),copy(),solve(); else l?b[r]=L:a[r]=L;
    	}
    	return 0;
    }
    

    又见食物链(chain.pas/cpp/c)

    经典水题,据说是NOI的题目。良心签到不解释

    对于这种层级分明的关系,考虑拓扑+DP转移。

    我们令(f_i)表示以(i)结尾的食物链的条数,发现转移时:

    (f_{son[i]}+=f_i)

    然后写一个拓扑转移即可,最后对于出度为(0)的点进行特判即可。

    但是注意一点:单节点不构成食物链

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    using namespace std;
    const int N=100005;
    struct edge
    {
    	int to,next;
    }e[N<<1];
    int head[N],in[N],out[N],q[N],n,m,x,y,cnt;
    unsigned long long f[N],ans;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; while (!isdigit(ch=tc()));
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void add(int x,int y)
    {
    	e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    }
    inline void reset(int now)
    {
    	for (register int i=head[now];i!=-1;i=e[i].next)
    	++f[e[i].to];
    }
    inline void top_sort(void)
    {
    	register int i; int H=0,T=0;
    	for (i=1;i<=n;++i)
    	if (!in[i]) q[++T]=i,reset(i);
    	while (H<T)
    	{
    		int now=q[++H];
    		for (register int i=head[now];i!=-1;i=e[i].next)
    		{
    			f[e[i].to]+=f[now];
    			if (!(--in[e[i].to])) q[++T]=e[i].to;
    		}
    	}
    }
    int main()
    {
    	freopen("chain.in","r",stdin); freopen("chain.out","w",stdout);
    	register int i; read(n); read(m);
    	memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e));
    	for (i=1;i<=m;++i)
    	{
    		read(x); read(y);
    		add(x,y); ++in[y]; ++out[x];
    	}
    	for (top_sort(),i=1;i<=n;++i)
    	if (!out[i]) ans+=f[i];
    	return printf("%lld",ans),0;
    }
    

    **OJ **中的目录 (oj.pas/cpp/c)

    我去题目怎么又是那么长。

    但是读懂题意之后你就会发现,这道题就是NOI2010 超级钢琴的树上版本。

    对于超级钢琴,不会的同学可以看一下sol,这里就不再赘述。

    然后在树上怎么弄,很简单的套路倍增

    考虑维护两个东西(father_{i,j})表示(i)向上(2^j)个点的节点,(f_{i,j})同理,表示的是最大值。

    然后我们确定这条链下方的点,那么对于上面的点就要求(sum_{father_{i,0}})最小了。还是倍增(log)级别解决。

    后面的堆等操作大同小异,其实关于树的题目倍增真的很万能

    CODE

    #include<cstdio>
    #include<cctype>
    #include<queue>
    using namespace std;
    const int N=500005,P=25;
    int n,m,l,r,sum[N],father[N][P],dep[N];
    long long ans;
    struct data
    {
    	int s,l,r,t;
    	bool operator <(const data x) const { return sum[x.s]-sum[father[x.t][0]]>sum[s]-sum[father[t][0]]; }
    };
    struct RMQ
    {
    	int x,num;
    }f[N][P];
    priority_queue<data> big;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    inline void RMQ_init(void)
    {
    	register int i,j;
    	for (j=1;j<P;++j)
    	for (i=1;i<=n;++i)
    	father[i][j]=father[father[i][j-1]][j-1];
    	for (j=1;j<P;++j)
    	for (i=1;i<=n;++i)
    	if (dep[i]>=1<<j) f[i][j]=f[i][j-1].x<f[father[i][j-1]][j-1].x?f[i][j-1]:f[father[i][j-1]][j-1];
    }
    inline int getfa(int x,int y)
    {
    	for (register int i=P-1;i>=0;--i)
    	if (y&(1<<i)) x=father[x][i];
    	return x;
    }
    inline int getmin(int x,int y)
    {
    	RMQ MIN=f[x][0];
    	for (register int i=P-1;i>=0;--i)
    	if (dep[father[y][i]]>=dep[x]) MIN=MIN.x<f[y][i].x?MIN:f[y][i],y=father[y][i];
    	return MIN.num;
    }
    int main()
    {
    	freopen("oj.in","r",stdin); freopen("oj.out","w",stdout);
    	register int i; read(n);
    	for (i=1;i<=n;++i) read(father[i][0]);
    	for (i=1;i<=n;++i) 
    	{
    		read(sum[i]); sum[i]+=sum[father[i][0]]; 
    		dep[i]=dep[father[i][0]]+1; f[i][0]=(RMQ){sum[father[i][0]],i};
    	}
    	read(m); read(l); read(r); RMQ_init();
    	for (i=1;i<=n;++i)
    	if (dep[i]>=l)
    	{
    		int L=getfa(i,min(r-1,dep[i]-1)),R=getfa(i,l-1);
    		big.push((data){i,L,R,getmin(L,R)});
    	}
    	while (m--)
    	{
    		data now=big.top(); big.pop(); ans+=sum[now.s]-sum[father[now.t][0]];
    		if (now.l^now.t) big.push((data){now.s,now.l,father[now.t][0],getmin(now.l,father[now.t][0])});
    		if (now.r^now.t) 
    		{
    			int next=dep[now.s]-dep[now.t]; next=getfa(now.s,next-1);
    			big.push((data){now.s,next,now.r,getmin(next,now.r)});
    		}
    	}
    	return printf("%lld",ans),0;
    }
    
  • 相关阅读:
    192021
    191020
    magento注册
    magento登陆
    把PHP的数组变成带单引号的字符串
    magento直接操作数据库
    兼容各大浏览器的event获取
    手动修改magento域名
    微信支付中的jsapi返回提示信息
    CentOS 下安装xdebug
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9278025.html
Copyright © 2020-2023  润新知