T1. 梦境
(dream.cpp/c/pas)
【问题描述】
智者奥尔曼曾说过:有缘的人即使相隔海角天涯,也会在梦境中相遇。
IcePrince_1968 和IcePrincess_1968 便是如此。有一天IcePrincess_1968 突发奇想:为什
么不用梦境操控仪器来增加她和IcePrince_1968 的缘分呢?
IcePrincess_1968 的梦境可以用n 个区间来表示,第i 个区间[li,ri]表示她的第i 个梦境会
在li 时刻开始,在ri 时刻结束(包含li 和ri 两个时刻)。因为IcePrincess_1968 经常做白日
梦,所以n 可能很大。
两个人的梦境不是什么时候都能融合的。只有在一些关键的与另一个人相关的梦境转折
点两个人的梦境相遇, 才能完成融合, 形成浪漫的梦境。IcePrincess_1968 探测到
IcePrince_1968 近期的m 个与IcePrincess_1968 相关的梦境转折点,第i 个转折点ti 表示他
的第i 个梦境转折点会在ti 时刻出现。因为IcePrince_1968 和IcePrincess_1968 很有缘,
IcePrince_1968 经常梦到IcePrincess_1968,所以m 可能会很大。
当IcePrincess_1968 的一个梦境包含了IcePrince_1968 的一个梦境转折点时,两个人的
这两段梦境就能得到融合。但要注意IcePrincess_1968 的每段梦境只能和IcePrince_1968 的
一个梦境转折点融合,类似的,IcePrince_1968 的每个梦境转折点只能和IcePrincess_1968
的一段梦境融合,否则会引发时空混乱。
IcePrincess_1968 很喜欢做和IcePrince_1968 相关的梦。所以她想算出她的这些梦境最
多能和IcePrince_1968 的梦境转折点融合出多少个浪漫的梦境。IcePrincess_1968 擅长文学
但不擅长计算机,所以只能找你帮忙。
【输入格式】
输入文件名为dream.in。
文件的第一行为有两个正整数n,m,表示IcePrincess_1968 的梦境个数和IcePrince_1968
的与IcePrincess_1968 相关的梦境转折点个数。
第2 至第n+1 行,每行两个正整数li,ri,第i+1 行的两个数刻画了IcePrincess_1968 的
第i 段梦境,含义如题面中所述。
第n+2 至第n+m+1 行,每行一个正数ti,第i 行的两个数刻画了IcePrince_1968 的第i-n-1
个梦境转折点,含义如题面中所述。
【输出格式】
输出文件名和dream.out。
输出文件仅一行,一个非负整数N 表示IcePrincess_1968 最多能获得多少段浪漫的梦境。
题解:
考场上,看到这题,我先想到的是不是什么贪心,而是线段树优化网络流建边。。。。
本来想直接开打的,后面想想,noip模拟真的会出这种东西吗?
(他一定是很良心的)但是其实远没有那么麻烦,考虑贪心。
我们将所有梦境区间按照左端点排序,将转折点也按照从小到大排序。
对于某一个转折点,我们需要找到左端点小于它的区间中,找到右端点比他大的最小的区间,则他一定是最优的。
我们从左到右扫过转折点,然后用一个堆来维护这些合法的区间,用一个指针扫扫就可以了。
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i++)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=2e5+4;
int n,m;
struct A{int l,r;bool operator<(A t)const{return r>t.r;}}a[N];
int t[N],ans; priority_queue<A> q;
bool cmp1(A t1,A t2){return t1.l<t2.l||(t1.l==t2.l&&t1.r<t2.r);}
int main(){
freopen("dream.in","r",stdin);freopen("dream.out","w",stdout);
g(n),g(m);
rep(i,1,n) g(a[i].l),g(a[i].r);
rep(i,1,m) g(t[i]);
sort(a+1,a+1+n,cmp1);sort(t+1,t+1+m);
int j=1;
rep(i,1,m){
while(j<=n&&a[j].l<=t[i]){q.push(a[j]);j++;}
while(!q.empty()&&q.top().r<t[i]) q.pop();
if(!q.empty()) ans++,q.pop();
}
return !printf("%d",ans);
}
T2. 玩具
(toy.cpp/c/pas)
【问题描述】
这个故事发生在很久以前,在IcePrincess_1968 和IcePrince_1968 都还在上幼儿园的时候。
IcePrince_1968 最近迷上了一种玩具,这种玩具中有两种零件:圆球和棍子。棍子的两头可
以插在两个圆球上的各一个空洞中,从而将两个圆球连接起来。为了保证玩具的娱乐性,任意一
个圆球上的空洞个数总是多于玩具套装中的棍子数。你可以认为圆球是没有体积的,所有棍子
的长度均为1。
IcePrince_1968 喜欢这样玩这种玩具:他先摸出玩具袋里的一个圆球放在地上,然后重复下
面的操作n-1 次:每次从袋中取出一个圆球和一根棍子,然后等概率的从地上的圆球中选择一个,
将该圆球和选择的圆球用棍子连起来,使得新的圆球在选中圆球的正上方。
IcePrince_1968 对自己搭出的艺术品很满意,便决定把这个物品送给IcePrincess_1968 作为
生日礼物。然而生日礼物是需要包装的,因为默认圆球没有体积,所以IcePrince_1968 不用考虑
包装盒的长和宽,但是包装盒的高是需要确定的,这里我们假设IcePrince_1968 是一个非常节俭
的孩子,所以包装盒的高总是等于艺术品的高度。IcePrince_1968 想知道自己需要的包装盒的高
的期望对质数p 取模后的值,但他还在上幼儿园,怎么会算呢,于是就请你来帮助他。
【输入格式】
输入文件名为toy.in。
输入数据仅一行,包含两个正整数n,p,表示最终的艺术品中圆球的个数和模数p。
【输出格式】
输出文件名为toy.out。
输入文件仅一行,一个正整数,表示包装盒的高的期望对质数p 取模后的值。
【输入输出样例1】
toy.in toy.out
3 998244353 499122178
【输入输出样例1 解释】
三个圆球组成的艺术品,高度只可能是1 或者2,所以高度的期望是1.5,在模998244353
下的期望是499122178。
【输入输出样例2】
见选手目录下的toy/toy2.in 和toy/toy2.ans。
【数据规模与约定】
对于30%的数据,满足n<=10,p<=1,000,007;
对于50%的数据,满足n<=20;
对于70%的数据,满足n<=50;
对于100%的数据,满足n<=200,p<=1,000,000,007,p 是质数。
题解:
神题
(orzJyc 省队爷&&北大爷考场拿到最高分)现在来讲正解:
设
表示有
个点,深度不超过
层的概率,
有
个点的森林,深度不超过
的概率。
然后还需要一个预处理:
设
表示对于有
个点的森林,有
个点在第一个子树内的概率。
则可以表示为:
然后又因为
可以从
直接转移过来,so:
答案即
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i--)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=304;
typedef long long ll;ll mod,n;
ll inv[N],f[N][N],h[N][N],dp[N][N];
int main(){
//freopen(".in","r",stdin);freopen(".out","w",stdout);
g(n),g(mod);inv[0]=inv[1]=1;dp[1][1]=1;
rep(i,2,n) inv[i]=(mod-mod/i)*inv[mod%i]%mod,(inv[i]+=mod)%=mod;
rep(i,2,n) rep(j,1,i){
dp[i][j]=dp[i-1][j-1]*(j-1)%mod*inv[i]%mod+dp[i-1][j]*(i-j)%mod*inv[i]%mod;
dp[i][j]%=mod;
}
rep(i,0,n) h[0][i]=1;
rep(i,1,n){
rep(j,0,n){
if(j) f[i][j]=h[i-1][j-1];
else f[i][j]=(i<2);
rep(k,1,i){
h[i][j]=(h[i][j]+f[k][j]*h[i-k][j]%mod*dp[i][k]%mod)%mod;
}
}
}
ll ans=0;
For(i,1,n){
ans+=(f[n][i]%mod-f[n][i-1]%mod)*i%mod;
ans%=mod;
}
return !printf("%lld",(ans+mod)%mod);
}
T3. 飘雪圣域
(icekingdom.cpp/c/pas)
【问题描述】
IcePrincess_1968 和IcePrince_1968 长大了,他们开始协助国王IceKing_1968 管理国内
事物。
IcePrincess_1968 和IcePrince_1968 住在一个宁静悠远的王国:IceKingdom —— 飘雪圣
域。飘雪圣域有n 个城镇,编号1,2,3...n。有些城镇之间有道路,且满足任意两点之间有且
仅有一条路径。飘雪圣域风景优美,但气候并不是太好。根据IcePrince_1968 的气候探测仪,
将来会发生q 场暴风雪。每场暴风雪可以用两个整数li,ri 刻画,表示这场暴风雪之后,只有
编号属于[li,ri]的城市没有受到暴风雪的影响。
在暴风雪的影响下迅速确定王国的农业生产方案是非常重要的事情。IceKing_1968 认
为,一个农业生产地域应该是一个极大连通块,满足每个节点都没有被暴风雪影响。这里极
大连通块的定义是:不存在一个不属于该点集的未被暴风雪影响的点与该连通块连通。
IcePrincess_1968 要负责算出每次暴风雪后,王国能拥有多少个农业生产地域。注意这
里每次暴风雪是独立的,即每次暴风雪过后,直到每个城镇重新焕发生机,下一次暴风雪才
会到来。
正如上文所述,IcePrincess_1968 擅长文学但不擅长计算机,于是请你帮忙。
【输入格式】
输入文件名为icekingdom.in。
第一行包含两个正整数n,q,表示IceKingdom 的城镇个数和暴风雪次数。
第2 至第n 行,每行两个正整数x,y,表示城镇x 和城镇y 之间有一条道路。
第n+1 至第n+q 行,每行两个正整数li,ri,描述一场暴风雪,含义如题面所述。
【输出格式】
输出文件名icekingdom.out。
输出文件共有q 行,第i 行表示在第i 场暴风雪之后农业生产地域的个数。【数据规模和约定】
对于30%的数据:n<=100,q<=100;
对于50%的数据:n<=2,000,q<=2,000;
对于100%的数据:n<=200,000,q<=200,000,对于所有的暴风雪,li<=ri。
题解:
又是一道神题。
首先应改要发现,这是一棵树。直接从联通块的角度考虑好像不太可以,于是可以从边的角度来解决。
考虑对于每一个询问,,只要知道两个端点都没有被暴雪影响到的边的条数
,就能算出连通块的个数。考虑每新加条边,就会少一个联通块,答案即是
.
然后考虑将询问离线,按照右端点从小到大排序。
用树状数组维护每个询问,将这个区间的左端点加入树状数组。
然后将边按照点编号较大的点的编号从小到大排序。
于是可以转化为,在询问区间有几条边存在。
于是就可以用树状数组。如果某条边右端点在询问区间内,将左端点插入树状数组,然后在询问一下就好了。
ps: 考场上还有一些神仙写出了主席树orzorzorz
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i--)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=2e5+4;
struct E{int l,r,id;}e[N],q[N];
int n,Q,t[N];
bool cmp(E a,E b){return a.r<b.r;}
in void ins(int x,int v){
for(;x<=n;x+=x&-x) t[x]+=v;
}
in int que(int x){
int res=0;
for(;x;x-=x&-x) res+=t[x];
return res;
}
int ans[N];
int main(){
//freopen(".in","r",stdin);freopen(".out","w",stdout);
g(n),g(Q);
For(i,1,n){
g(e[i].l),g(e[i].r);
if(e[i].l>e[i].r) swap(e[i].l,e[i].r);
}
rep(i,1,Q){
g(q[i].l),g(q[i].r),q[i].id=i;
if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
}
sort(e+1,e+n,cmp);sort(q+1,q+1+Q,cmp);
int j=1;
rep(i,1,Q){
while(q[i].r>=e[j].r&&j<n) ins(e[j].l,1),j++;
int s=que(q[i].r)-que(q[i].l-1);
ans[q[i].id]=q[i].r-q[i].l+1-s;
}
rep(i,1,Q) printf("%d\n",ans[i]);
return 0;
}