NOIP 2018训练赛第四场

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/82936897

正题

     A. 求和

       给一个 n 行 t 列的矩阵,矩阵第 i 行第 j 列的元素是 i+j。

       定义矩阵第 i 行的积为第 i 行所有元素的乘积。 

       现在要你求矩阵所有行的积的和。答案可能很大,所以 mod 1000000007输出。

       100%, 1≤T≤10000 1≤n≤1e10, 1≤t≤1000

       也就是求:\sum_{i=1}^n \frac{(i+t)!}{i!} \\=\sum_{i=1}^n C(i+t,i)*t! \\=t!(\sum_{i=0}^n \binom{i+t}{i}-1) \\=t!(\binom{n+t+1}{t+1}-1)

     B. 刷题

       你打开了某著名 OJ 准备刷题。

       这个 OJ 上一共有 nn 道题,并且你知道你刷第 ii 道题会获得 aiai 的智慧值。对于每道题你都有两种选择:嘴巴切题和老实码完,第 ii 题老实码完需要花费时间 titi,但是嘴巴切它只需要花费 ⌈ti2⌉⌈ti2⌉。一道题不管是嘴巴切还是老实码,都可以获得智慧值,但是你也不想做一个嘴巴选手,所以你允许自己最多嘴巴切 ww 个题。

       你算了下自己总共有 SS 的时间,并且你有强迫症,只想从某道题开始顺着往下做,也就是你只想做连续一段的题。问在这 SS 时间内最多可以获得多少智慧值。

       用两个set维护即可,S1,S2,S1记录的是直接做的,S2记录的是口胡的,然后用two-pointer维护,然后从S2中选一个最小的,放到S1里面,然后维护就可以了。

       在比赛中我是用树状数组+二分维护的,双log,打错了一个-,所以就没有过。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define lowbit(i) (i&(-i))
using namespace std;

int n,w,s;
int a[200010];
int t[200010];
int p[10010];
int b[10010];
int c[10010];
int temp=10000;
int ans=0;
int nowa=0,nowt;

void add(int x,int tp){
	int as;
	for(int i=x;i<=temp;i+=lowbit(i)){
		p[i]+=tp;
		as=temp-x+1;
		b[i]+=as*tp;
		c[i]+=(as%2==0?as/2:as/2+1)*tp;
	}
}

int get_sum(int *now,int t){
	int tot=0;
	for(int i=t;i>=1;i-=lowbit(i)) tot+=now[i];
	return tot;
}

int check(){
	int l=1,r=10000,ans=0,mid;
	while(l<=r){
		mid=(l+r)/2;
		if(get_sum(p,mid)>=w){
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return ans;
}

int main(){
	scanf("%d %d %d",&n,&w,&s);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&t[i]);
	int L=1,R=0,we;
	while(R<=n){
		while(R<=n){
			R++;
			nowa+=a[R];nowt+=t[R];
			we=temp-t[R]+1;add(we,1);
			if(R-L+1<=w) we=get_sum(c,temp);
			else {
				we=check();
				we=nowt-get_sum(b,we)+get_sum(c,we)+(get_sum(p,we)-w)*((temp-we+1)/2);
			}
			if(we>s) break;
			else ans=max(ans,nowa);
		}
		while(L<=R){
			nowa-=a[L];nowt-=t[L];
			we=temp-t[L]+1;add(we,-1);
			if(R-L+1<=w) we=get_sum(c,temp);
			else {
				we=check();
				we=nowt-get_sum(b,we)+get_sum(c,we)+(get_sum(p,we)-w)*((temp-we+1)/2);
			}
			L++;
			if(we<=s) {
				ans=max(ans,nowa);
				break;
			}
		}
	}
	printf("%d",ans);
}

    C. 数叶子

      给你一棵 nn 个点的无根树,点的编号是 1−n1−n。叶子指的是一颗有根树没有孩子的结点。一个友善的叶子集合是由一些叶子组成的非空集合,且满足,这些叶子,在某种 dfs 顺序下(即存在一种 dfs 顺序),按 dfs 顺序取出所有叶子,友善的叶子集合里的叶子在这里面是连续的(顺序无所谓)。现在问你,当这棵树的根是 ii 的时候,一共有多少种友善的叶子集合。答案mod 10^9+7

      考虑对于每棵子树,是选一边,选全部还是不选。很麻烦,直接暴力60

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/82936897