zoukankan      html  css  js  c++  java
  • EZ 2018 06 02 NOIP2018 模拟赛(十七)

    这次的比赛是真心比较狗,我TM的写了30min的树剖ZZ地直接memset超时了

    话说我既然想到差分就应该去写差分的啊!

    好了不过这次Rank还挺高的,终于要打进前10了当然是假的了

    好了下面开始讲题。链接

    A. 幻灯结界

    一道非常基础的贪心题,我们只需要两次贪心即可解决。

    首先是对于改变防御值的问题,我们可以用一个很显然并且正确的贪心:总是把机会给最小的并且这个最小值应该小于改变的值。否则就没有得到最大的利用。

    然后对于下一步我们可以通过一个叫排序不等式的东西。说白了就是:大的对大的,小的对小的。这样就可以保证(max(b_i-a_i))最小。

    其实上面的东西只需要用正常人的思想感性理解一下即可。

    然后我们就sort一下,然后扫一遍即可。

    CODE

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=200005;
    int a[N],b[N],n,x,y,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=tc();
    	while (ch<'0'||ch>'9') ch=tc();
    	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline int max(int a,int b)
    {
    	return a>b?a:b;
    }
    int main()
    {
    	//freopen("A.in","r",stdin); freopen("A.out","w",stdout);
    	register int i; read(n); read(x); read(y);
    	for (i=1;i<=n;++i)
    	read(a[i]); sort(a+1,a+n+1);
    	for (i=1;i<=n;++i)
    	read(b[i]); sort(b+1,b+n+1);
    	for (i=1;i<=n&&x>0;++i)
    	{
    		if (a[i]>=y) break;
    		a[i]=y; --x;
    	}
    	sort(a+1,a+n+1);
    	for (i=1;i<=n;++i)
    	ans=max(ans,b[i]-a[i]);
    	printf("%d",ans);
    	return 0;
    }
    
    

    B. 时之终末

    一道很不错的状压DP的题。

    首先我们发现数据范围:(a<=16),所以果断状压。

    (f_{i,j})表示前(i)个数中末尾(j)个数的情况((j)不足则在前面补0),其中(j)是一个二进制数

    其每一位分别表示对应位置上的数是否被选,例如当(j=6)时:

    代表的状态为(110),即表示这个数位置(x)之前的(a_{x-2})(a_{x-1})都被选择了(不足则补0)

    所以我们就能比较轻易地写出转移方程:

    • (f_{i,k}=max(f_{i,k},f_{i-1,j}+i>=a?s_k:0))(表示不选(i)这个位置上的数)
    • (f_{i,k|1}=max(f_{i,k|1},f_{i-1,j}+a_i+i>=a?s_k:0))(表示选(i)这个位置上的数)

    其中,(k=j<<1)。其实就是把之前的状态都往前1位然后把新的添加进来。

    (s_k)表示当状态是(k)时,通过额外方式获得的奖励分数

    那么接下来就是如何枚举这个(s_k)的问题了。

    我们可以先分别处理出所有状态的情况,然后通过经典的枚举子集方法来解决。

    我们设(t_i)表示一种状态为(i)的奖励价值,则有:

    for (i=0;i<=cnt;++i)
    for (j=i;j!=0;j=(j-1)&i)
    s[i]+=t[j];
    

    以上预处理的复杂度为(O(3^a))。然后我们滚存一下(f_{i,j})即可

    CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=105,M=55,STATE=(1<<16)+5;
    int f[2][M][STATE],v[N],c[STATE],t[STATE],n,m,a,b,x,y,z,cnt,s,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=tc(); int flag=1;
    	while (ch<'0'||ch>'9') { if (ch=='-') flag=-1; ch=tc(); }
    	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc(); x*=flag;
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    inline int max(int a,int b)
    {
    	return a>b?a:b;
    }
    int main()
    {
    	//freopen("B.in","r",stdin); freopen("B.out","w",stdout);
    	register int i,j,k;
    	read(n); read(m); read(a); read(b);
    	for (i=1;i<=n;++i)
    	read(v[i]);
    	for (i=1;i<=b;++i)
    	{
    		read(x); read(y); s=0;
    		for (j=1;j<=x;++j)
    		read(z),s|=1<<(a-z);
    		t[s]+=y;
    	}
    	cnt=(1<<a)-1;
    	for (i=0;i<=cnt;++i)
    	for (j=i;j!=0;j=(j-1)&i)
    	c[i]+=t[j];
    	memset(f[0],163,sizeof(f[0])); int INF=f[0][0][0]; f[0][0][0]=0;
    	for (i=1;i<=n;++i)
    	{
    		int now=i&1,lst=now^1; memset(f[now],163,sizeof(f[now]));
    		for (j=0;j<=min(i-1,m);++j)
    		for (k=0;k<=cnt;++k)
    		if (f[lst][j][k]!=INF)
    		{
    			s=(k<<1)&cnt;
    			f[now][j][s]=max(f[now][j][s],f[lst][j][k]+(i>=a?c[s]:0));
    			f[now][j+1][s|1]=max(f[now][j+1][s|1],f[lst][j][k]+v[i]+(i>=a?c[s|1]:0));
    		}
    	}
    	for (ans=INF,i=0;i<=m;++i)
    	for (j=0;j<=cnt;++j)
    	ans=max(ans,f[n&1][i][j]);
    	printf("%d",ans);
    	return 0;
    }
    

    C. 伪神

    一道需要思考的题目,告诉我们有些东西是暴力树剖也剖不过去的。

    首先这题目一眼就可以看出上树剖的可能性,然后我们根据题意直接主席树修改最后查询大于t的数有即可

    其实这个是一个树上差分的板子题。

    我们先来讲一下差分这个东西(注意这不是差分约束)。我们现在开始考虑一个问题,我们对线性上的一段区间实行全部+1的操作,然后这个数组的值的变化。

    我们可以发现,操作的区间(x,y)之间的数它们之间的相对值是不变的

    然后我们再结合前缀和的思想,令一个前缀和数组(sum_i)。然后我们每次操作的时候只需要把(sum_x+1)然后(sum_{y+1-1})即可

    最后我们只需要累加这个(sum)数组的值即可

    然后对于树上的操作其实也是一样的,我们只需要在树剖过后的序列上差分即可

    但是注意最后这个统计的过程是(O(n))的,那么复杂度就让人受不了了。

    我们再次利用差分的重要性质:操作区间内的数之间的相对着不变,我们每一次也只需要把操作过的区间单独做一下即可。中间那些没有用的直接弹掉即可。

    然后就可以用一种类似于离散化并去重的方法最后sort一下累加区间即可

    不过这样由于sort的复杂度只能得96pts,AC需要更高级的离线基排,这里只写了sort的96分版

    具体操作还是看CODE吧

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int N=100005;
    struct edge
    {
    	int to,next;
    }e[N<<1];
    int n,m,a,b,x,y,t,cnt,tot,ans,rt=1,num,sum[N],head[N],dep[N],son[N],size[N],father[N],top[N],id[N],q[N*5];
    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=tc(); int flag=1; 
    	while (ch<'0'||ch>'9') { if (ch=='-') flag=-1; ch=tc(); }
    	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc(); x*=flag;
    }
    inline void write(int x)
    {
    	if (x/10) write(x/10);
    	putchar(x%10+'0');
    }
    inline void add(int x,int y)
    {
    	e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    }
    inline void swap(int &a,int &b)
    {
    	int t=a; a=b; b=t;
    }
    inline void modify(int x,int y)
    {
    	q[++num]=x; q[++num]=y+1; ++sum[x]; --sum[y+1];
    }
    inline void DFS1(int now,int fa,int d)
    {
    	father[now]=fa; dep[now]=d; size[now]=1; int res=-1;
    	for (register int i=head[now];i!=-1;i=e[i].next)
    	if (e[i].to!=fa)
    	{
    		DFS1(e[i].to,now,d+1);
    		size[now]+=size[e[i].to];
    		if (size[e[i].to]>res) res=size[e[i].to],son[now]=e[i].to;
    	}
    }
    inline void DFS2(int now,int topf)
    {
    	id[now]=++tot; top[now]=topf;
    	if (!son[now]) return;
    	DFS2(son[now],topf);
    	for (register int i=head[now];i!=-1;i=e[i].next)
    	if (e[i].to!=father[now]&&e[i].to!=son[now]) DFS2(e[i].to,e[i].to);
    }
    inline void change(int x,int y)
    {
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		modify(id[top[x]],id[x]); x=father[top[x]];
    	}
    	if (dep[x]<dep[y]) swap(x,y);
    	modify(id[y],id[x]);
    }
    inline void updata(int x)
    {
    	modify(id[x],id[x]+size[x]-1);
    }
    int main()
    {
    	//freopen("C.in","r",stdin); freopen("C.out","w",stdout);
    	register int i; read(n); read(m);
    	memset(e,-1,sizeof(e));
    	memset(head,-1,sizeof(head));
    	for (i=1;i<n;++i)
    	read(x),read(y),add(x,y),add(y,x);
    	DFS1(rt,-1,0); DFS2(rt,rt);
    	while (m--)
    	{
    		read(a); read(b); read(t);
    		for (i=1;i<=a;++i)
    		read(x),read(y),change(x,y);
    		for (i=1;i<=b;++i)
    		read(x),updata(x);
    		q[++num]=1; q[++num]=n+1;
    		sort(q+1,q+num+1); num=unique(q+1,q+num+1)-q-1;
    		for (x=0,i=1;i<num;++i)
    		x+=sum[q[i]],ans+=x>=t?q[i+1]-q[i]:0;
    		write(ans); putchar('
    ');
    		for (i=1;i<=num;++i)
    		sum[q[i]]=0; num=ans=0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    时间复杂度的分析
    插入排序
    maven中jar、war、pom的区别
    Maven属性(properties)标签的使用
    超级POM
    maven 常用命令
    Maven Pom文件标签详解
    maven 查找依赖的办法
    maven snapshot和release版本的区别
    maven pom文件标签含义
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9152588.html
Copyright © 2011-2022 走看看