2018.10.17--多校联测第二场测试总结

T1:贪心裸题,估计100分,实际得分100分,原题戳
T2:Dp,估计50分,实际得分50分,暂时没有在任何OJ上见过相似的题目。
T3:二维点计数,估计100分,实际得分100分,没有见过类似题目

T1题解:把区间按r为第一关键字,l为第二关键字排序,转折点从小到大排序对于两个点x和y能满足当前区间,那么对于后面的区间只有三种情况:
1、x和y都可用
2、x和y都不可用
3、x不可用,y可用
所以选择更小的x明显更优,用stl去找x就可以了,代码如下:

#include <set>
#include <cstdio>
#include <algorithm>
using namespace std;
 
const int maxn=2e5+5;
 
int n,m,ans;
multiset<int>s;
multiset<int>::iterator it;
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
struct cows {
    int l,r;
 
    bool operator<(const cows &a) const {
        if(r==a.r)return l<a.l;
        return r<a.r;
    }
}c[maxn];
 
int main() {
	freopen("dream.in","r",stdin);
	freopen("dream.out","w",stdout);
    m=read();n=read();
    for(int i=1;i<=m;i++)
        c[i].l=read(),c[i].r=read();
    sort(c+1,c+m+1);
    for(int i=1;i<=n;i++) {
        int tmp=read();
        s.insert(tmp);
    }
    for(int i=1;i<=m;i++) {
        it=s.lower_bound(c[i].l);
        if(it!=s.end()&&*it<=c[i].r)
            ans++,s.erase(it);
    }printf("%d\n",ans);
    return 0;
}

T2:预处理dp[i][j]表⽰i个点的森林,有j个点在第⼀棵树的概率,转移考虑第i个点是否在第⼀棵⼦树中,我们有状态转移⽅程

\[dp[i][j]=dp[i-1][j-1]*(j-1)*inv[i]+dp[i-1][j]*(i-j)*inv[i] \]

考虑修改算法三中状态的含义,令f[i][j]表⽰有i个点的树,深度不超过j的概率,g[i][j]表⽰有i个点的森林,深度不超过j的概率,f[i][j]直接从g[i-1][j-1]转移
来;g[i][j]考虑枚举第⼀棵树的⼤⼩k,从⼀棵树和⼀个森林转移来,同时还要乘上第⼀棵⼦树⼤⼩为k的概率,我们有状态转移⽅程:

\[g[i][j]=\sum\limits_{k=1}^if[k][j]*g[i-k][j]*dp[i][k] \]

具体的细节可以⻅标程。最后只要⽤f[n][j]-f[n][j-1]就可以得到深度为j的树的概率。
代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;

int n,pps;
int inv[205],f[205][205];
int dp[205][205],g[205][205];

int read() {
	int x=0,f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
	return x*f;
}

void prepare() {
	inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++)
		inv[i]=1ll*(pps-pps/i)*inv[pps%i]%pps;
}

int main() {
	n=read(),pps=read();
	prepare();
	dp[1][1]=1;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++) {
			dp[i][j]=1ll*dp[i-1][j-1]*(j-1)%pps*inv[i]%pps;
			dp[i][j]=(dp[i][j]+1ll*dp[i-1][j]*(i-j)%pps*inv[i]%pps)%pps;
		}
	for(int i=0;i<=n;i++)
		g[0][i]=g[1][i]=f[0][i]=f[1][i]=1;
	g[1][0]=0;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=n;j++) {
			f[i][j]=g[i-1][max(0,j-1)];
			for(int k=1;k<=i;k++)
				g[i][j]=(g[i][j]+1ll*f[k][j]*g[i-k][j]%pps*dp[i][k]%pps)%pps;
		}
	int ans=pps-1;
	for(int i=2;i<=n;i++) {
		int tmp=((f[n][i]-f[n][i-1])%pps+pps)%pps;
		ans=(ans+1ll*tmp*i%pps)%pps;
	}
	ans=(ans%pps+pps)%pps;
	printf("%d\n",ans);
	return 0;
}

T3:对于这道题有个非常简单的性质然而我考场上居然想了2小时!!!!,那就是对于一个区间[l,r]的答案就是r-l+1-cnt,cnt就是两端都在[l,r]内的边的个数。知道这个性质就是个二维偏序裸题了………………
代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
#define low(i) ((i)&(-i))

const int maxn=2e5+5;

int n,q;

int read() {
	int x=0,f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
	return x*f;
}

struct fake {
	int opt,id;
	int l,r,ans;
	
	void init(int a,int _id) {
		l=read(),r=read();
		if(l>r)swap(l,r);
		opt=a;id=_id;
	}
	
	bool operator<(const fake &a)const {
		if(r==a.r)return opt<a.opt; 
		return r<a.r;
	}
}p[maxn*2];

struct Tree_array {
	int c[maxn];
	
	void add(int pos) {
		for(int i=pos;i<=n;i+=low(i))
			c[i]++;
	}
	
	int query(int pos) {
		int sum=0;
		for(int i=pos;i;i-=low(i))
			sum+=c[i];
		return sum;
	}
}T;

bool cmp(fake a,fake b) {
	return a.id<b.id;
}

int main() {
	freopen("icekingdom.in","r",stdin);
	freopen("icekingdom.out","w",stdout);
	n=read(),q=read();
	for(int i=1;i<n;i++)
		p[i].init(0,0);
	for(int i=n;i<n+q;i++)
		p[i].init(1,i);
	sort(p+1,p+n+q);
	for(int i=1;i<n+q;i++) {
		if(p[i].opt) p[i].ans=p[i].r-p[i].l+1-(T.query(p[i].r)-T.query(p[i].l-1));
		else T.add(p[i].l);
	}
	sort(p+1,p+n+q,cmp);
	for(int i=n;i<n+q;i++)
		printf("%d\n",p[i].ans);
	return 0;
}

posted on 2018-10-17 19:25  HYSBZ_mzf  阅读(126)  评论(0编辑  收藏  举报

导航