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;
}