• 题解 noip2018模拟测试赛(三十五)


    传送门

    Yakiniku Restaurants

    题目思路

    容易发现走回头路一定不会更优,因此答案走过的路程一定为一段区间

    考虑 (O(n^2))​ 暴力枚举区间,再 (O(m))​​ 计算每种餐票能获得的最大值,时间复杂度为 (O(n^2m)) ,卡不过

    考虑dp优化,假设对于餐厅 (i) 来说,从餐厅 (j) 走到餐厅 (i)​​ 能获得最大的价值,那么称 (j)(i) 的最优决策点

    我们发现,这个dp是具有决策单调性的,也就是说若 (j)​​ 为 (i)​​ 的最优决策点,那么 (i+1)​​​ 的最优决策点一定在 (j)​ 之后

    假设餐厅 (j) 走到餐厅 (i) 能获得最大的餐票价值为 (bleft(i,j ight)) ,走过的路程为 (aleft(i,j ight))

    因为我们发现,既然 (i) 的最优决策点不为 (j) 之前的店铺,说明对于 (i) 来说,假设 (k<j) ,那 (b(i,k)-b(i,j)) 一定小于 (a(i,k)-a(i,j)=a(j,k)) ,即 (j) 之前的店铺餐票带来的价值上升一定比不过路程带来的价值下降;那么对于 (i+1) 来说 (b(i+1,k)-b(i+1,j)le b(i,k)-b(i,j)le a(j,k)le a(i+1,k)-a(i+1,j))

    (calc(i,j))​ 为区间 ([i,j])​ 的最大价值,我们直接st表预处理一下对于每种餐票各个区间的最大值即可 (O(m))(calc)

    因为求解 (calc)​ 不需要之前决策点的信息,因此我们分治处理。对于区间 ([l,r])​ ,我们对于点 (mid)​ 求出最优决策点 (p)​ ,那么区间 ([l,mid-1])​ 的最优决策点显然在 (p)​ 之前,区间 ([mid+1,r])​ 的最优决策点显然在 (p)​​ 之后,这样的时间复杂度是 (O(nlogn)) 的,再加上求解 (calc)(O(m)) ,总的时间复杂度即为 (O(nmlogn))

    PS: 似乎还有另一种方法,将区间 ([l,r]) 转化为二维上的一个点 ((l,r))​ ,枚举每种餐票,通过单调栈求解左右侧首个最大值,通过差分的技巧插入二维平面,然后就可以 (O(1))​ 查询 (calc)​ 了,预处理时间复杂度 (O(nm)),询问 (O(n^2))

    代码

    #include<iostream>
    using namespace std;
    int n,m,dp[5005];
    long long a[5005],b[5005][305];
    long long lg2[5005]= {-1},h[305][5005][15];
    void st(int t) {
    	for(int i=1; i<=n; i++) {
    		h[t][i][0]=b[i][t];
    	}
    	for(int j=0; j<lg2[n]; j++) {
    		int mx=n-(1<<(j+1))+1;
    		for(int i=1; i<=mx; i++) {
    			h[t][i][j+1]=max(h[t][i][j],h[t][i+(1<<j)][j]);
    		}
    	}
    }
    long long getmax(int t,int l,int r) {
    	int k=lg2[r-l+1];
    	return max(h[t][l][k],h[t][r-(1<<k)+1][k]);
    }
    long long calc(int l,int r) {
    	if(l>r)return 0;
    	long long ans=a[l]-a[r];
    	for(int i=1; i<=m; i++) {
    		ans+=getmax(i,l,r);
    	}
    	return ans;
    }
    long long ans;
    void solve(int l,int r,int x,int y) {
    	if(x>y)return;
    	int mid=(x+y)/2;
    	long long mx=0;
    	int now=l;
    	for(int i=l; i<=r&&i<=mid; i++) {
    		long long c=calc(i,mid);
    		if(c>=mx)mx=c,now=i;
    	}
    	ans=max(ans,mx);
    	solve(l,now,x,mid-1);
    	solve(now,r,mid+1,y);
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	for(int i=2; i<=n; i++) {
    		scanf("%lld",&a[i]);
    		a[i]+=a[i-1];
    	}
    	for(int i=1; i<=n; i++) {
    		for(int j=1; j<=m; j++) {
    			scanf("%lld",&b[i][j]);
    		}
    	}
    	for(int i=1; i<=n; i++)lg2[i]=lg2[i/2]+1;
    	for(int i=1; i<=m; i++)st(i);
    	solve(1,n,1,n);
    	printf("%lld",ans);
    	return 0;
    }
    

    Decrementing

    题目思路

    如果没有除最大公约数的操作,那么直接判断 (a_i-1) 的和的奇偶性即可

    因此我们思考除最大公约数的操作会对 (a_i-1) 的和的奇偶性产生什么影响

    我们发现,当最大公约数为奇数时操作对 (a_i-1)​ 的和的奇偶性无影响,只有当其是偶数时才有可能产生影响

    而所有数的最大公约数为偶数当且仅当所有数都为偶数,因此只有当前局面中只有一个非一奇数时奇偶性才有可能变化

    并且如果当前情况对先手而言是必胜的话,他永远可以通过破坏偶数的方式使得不可能出现最大公约数为偶数,维持必胜局面

    那么当没有可能变化时直接判断,有可能变化时我们模拟即可,由于每次至少除二,因此最多只会模拟 (O(nlogn))​ 次,总时间复杂度为 (O(nlogn))

    代码

    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,a[100005];
    int jishu() {
    	int shu=0;
    	for(int i=1; i<=n; i++) {
    		if(a[i]==1)return 0;
    		shu+=(a[i]&1);
    	}
    	return shu;
    }
    void jian() {
    	for(int i=1; i<=n; i++) {
    		if(a[i]&1)a[i]--;
    	}
    }
    void chu() {
    	int gongyinshu=a[1];
    	for(int i=2; i<=n; i++) {
    		gongyinshu=__gcd(gongyinshu,a[i]);
    	}
    	for(int i=1; i<=n; i++) {
    		a[i]/=gongyinshu;
    	}
    }
    int main() {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		scanf("%d",&a[i]);
    	}
    	for(int wanjia=1; 1; wanjia^=1) {
    		long long he=0;
    		for(int i=1; i<=n; i++) {
    			he+=a[i]-1;
    		}
    		if(he&1) {
    			if(wanjia)printf("First");
    			else printf("Second");
    			break;
    		} else {
    			if(jishu()==1) {
    				jian(),chu();
    			} else {
    				if(!wanjia)printf("First");
    				else printf("Second");
    				break;
    			}
    		}
    	}
    	return 0;
    }
    

    wangxz与OJ

    题目思路

    数据结构题

    由于题目没有保证插入的数量与删除的数量,因此不能像维护序列那样直接插入,删除

    发现题目插入的都是一段区间,因此我们平衡树上的一个节点维护的就是一个区间

    对于序列题,采用无旋treap,另外split时有可能将一个区间劈成两个,需要将原来的区间分成两个区间

    注意一下数组大小即可

    代码

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    using namespace std;
    const int N=1000005;
    int cnt,rt,ls[N],rs[N],rd[N];
    int l[N],r[N],size[N];
    void pushup(int p) {
    	size[p]=size[ls[p]]+size[rs[p]]+r[p]-l[p]+1;
    }
    int add(int a,int b) {
    	size[++cnt]=b-a+1;
    	rd[cnt]=rand();
    	l[cnt]=a,r[cnt]=b;
    	return cnt;
    }
    void split(int p,int &a,int &b,int k) {
    	if(p==0) {
    		a=b=0;
    		return;
    	}
    	if(k<=size[ls[p]]) {
    		b=p;
    		split(ls[p],a,ls[b],k);
    	} else {
    		if(k<size[ls[p]]+(r[p]-l[p]+1)) {
    			int p2=add(l[p]+k-size[ls[p]],r[p]);
    			r[p]=l[p]+k-size[ls[p]]-1;
    			rs[p2]=rs[p],rs[p]=p2;
    		}
    		a=p;
    		split(rs[p],rs[a],b,k-size[ls[p]]-(r[p]-l[p]+1));
    	}
    	pushup(p);
    }
    int merge(int a,int b) {
    	if(a==0||b==0) {
    		return a|b;
    	}
    	if(rd[a]<rd[b]) {
    		rs[a]=merge(rs[a],b);
    		pushup(a);
    		return a;
    	} else {
    		ls[b]=merge(a,ls[b]);
    		pushup(b);
    		return b;
    	}
    }
    void print(int p) {
    	if(p==0)return;
    	print(ls[p]);
    	for(int i=l[p]; i<=r[p]; i++) {
    		printf("%d ",i);
    	}
    	print(rs[p]);
    }
    int main() {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<=n; i++) {
    		int x;
    		scanf("%d",&x);
    		rt=merge(rt,add(x,x));
    	}
    	while(m--) {
    		int op,p,a,b;
    		scanf("%d",&op);
    		int x,y,z;
    		if(op==0) {
    			scanf("%d%d%d",&p,&a,&b);
    			split(rt,x,y,p);
    			rt=merge(x,merge(add(a,b),y));
    		}
    		if(op==1) {
    			scanf("%d%d",&a,&b);
    			split(rt,y,z,b);
    			split(y,x,y,a-1);
    			rt=merge(x,z);
    		}
    		if(op==2) {
    			scanf("%d",&p);
    			split(rt,y,z,p);
    			split(y,x,y,p-1);
    			printf("%d
    ",l[y]);
    			rt=merge(x,merge(y,z));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    mysql 防止插入某个字段重复的值
    vue 脚手架的目录结构
    搭建Vue 脚手架项目
    flex 布局的页面
    Java List 排序问题
    maven 管理oracle jar
    JPA 注解
    PL/SQL Developer 不显示系统表,默认显示My objects
    jquery面试题
    web前端课程检测2
  • 原文地址:https://www.cnblogs.com/ezlmr/p/15118556.html
Copyright © 2020-2023  润新知