[CSP-S模拟测试74]题解

A.梦境

如果不用去重一定要用Multiset……挂30分算是出题人手下留情了。

贪心。把点排序,区间按右端点递增排序。依次考虑每个区间,取能选的最靠左的点即可。multiset维护。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
	return x*f;
}
const int N=2e5+5;
int n,m,t[N],vis[N],ans;
struct inv
{
	int l,r;
	friend bool operator < (const inv &x,const inv &y)
	{
		return x.r<y.r;
	}
}a[N];
multiset<int> s;
multiset<int>::iterator it1,it2;
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i].l=read(),a[i].r=read();
	for(int i=1;i<=m;i++)
		t[i]=read();
	sort(t+1,t+m+1);
	for(int i=1;i<=m;i++)
		s.insert(t[i]);
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(s.empty())break;
		it1=s.lower_bound(a[i].l);
		it2=s.upper_bound(a[i].r);
		if(it1==s.end())continue;
		if(it2!=s.begin())it2--;
		else continue;
		if((*it1)>(*it2))continue;
		ans++;
		s.erase(it1);
	}
	cout<<ans<<endl;
	return 0;
}

B.玩具

让我懒癌发作的神dp

注意问题的转化方式以及辅助数组的正确使用姿势。

比较难的计数往往需要探究已给出条件的一些性质,并把抽象的转移过程具体化。本题的$f[i][j]=g[i-1][j-1]$就是一个很好的例子。

还有就是状态的正确设定,计数一般不会有特别鬼畜的转移方式,所以状态数组的含义十分重要。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=205;
ll n,mod;
ll dp[N][N],g[N][N],f[N][N],inv[N];
// dp:in the 1st tree
// f:tree‘s depth <=j
// g:forest‘s depth <=j
ll qpow(ll a,ll b)
{
    ll res=1;a=a%mod;
    while(b)
    {
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&n,&mod);
    dp[1][1]=f[1][0]=1;
    for(int i=1;i<=n;i++)
        inv[i]=qpow(i,mod-2);
    for(int i=2;i<=n;i++)
        for(int j=1;j<=i;j++)
            dp[i][j]=(dp[i-1][j-1]*1LL*(j-1)%mod*inv[i]%mod+dp[i-1][j]*1LL*(i-j)%mod*inv[i]%mod)%mod;
    for(int i=0;i<=n;i++)
        g[0][i]=1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=n;j++)
        {
            if(j)f[i][j]=g[i-1][j-1];
            for(int k=1;k<=i;k++)
                (g[i][j]+=f[k][j]*g[i-k][j]%mod*dp[i][k]%mod)%=mod;
        }
    ll ans=0;
    for(int i=1;i<=n;i++)
        (ans+=(f[n][i]-f[n][i-1]+mod)%mod*i%mod)%=mod;
    cout<<ans<<endl;
    return 0;
}

C.飘雪圣域

森林的联通块个数=点数-边数,记住了!!

上次考场能YY出来这次却想不到QAQ……

那么对于每次询问,点数已知,只需要求出这些点之间有多少边即可。

设一条边连接了$x$和$y$。那么把边排序,以$x$为下标把$y$上传到主席树上,每次查询区间有多少数 $\leq y$即可。

连图都不用建2333

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
    return x*f;
}

const int N=2e5+5;
int n,Q;
struct edge
{
    int x,y;
    friend bool operator < (const edge &a,const edge &b)
    {
        return (a.x==b.x)?(a.y<b.y):(a.x<b.x);
    }
}e[N<<1];
int root[N<<5],ls[N<<5],rs[N<<5],size[N<<5],pos[N],type;
void update(int &k,int l,int r,int old,int val)
{
    k=++type;
    ls[k]=ls[old];rs[k]=rs[old];
    size[k]=size[old]+1;
    if(l==r)return ;
    int mid=l+r>>1;
    if(val<=mid)update(ls[k],l,mid,ls[old],val);
    else update(rs[k],mid+1,r,rs[old],val);
}
int query(int k,int l,int r,int val)
{
    if(l==r)return size[k];
    int mid=l+r>>1;
    if(val<=mid)return query(ls[k],l,mid,val);
    else return size[ls[k]]+query(rs[k],mid+1,r,val);
}
int main()
{
    n=read();Q=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[i]=(edge){min(x,y),max(x,y)};
    }
    sort(e+1,e+n);
    for(int i=1;i<n;i++)
    {
        update(root[i],1,n,root[i-1],e[i].y);
        if(!pos[e[i].x])pos[e[i].x]=i;
    }
    pos[n]=n;root[n]=root[n-1];
    for(int i=n;i;i--)
        if(!pos[i])pos[i]=pos[i+1];
    while(Q--)
    {
        int l=read(),r=read();
        int lc=pos[l],rc=pos[r];
        int num=query(root[rc],1,n,r)-query(root[lc-1],1,n,r);
        printf("%d\n",r-l+1-num);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Rorschach-XR/p/11679411.html

时间: 2024-11-10 12:05:38

[CSP-S模拟测试74]题解的相关文章

[CSP-S模拟测试59]题解

以后题解还是单独放吧. A.Divisors 根号筛求所有数的因子,扫一遍去重统计即可. #include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<map> using namespace std; const int N=205; int a[N],m,n; map<int,int> bu; vector<int> re

CSP-S 模拟测试57题解

人生第一次A,B层一块考rank2,虽然说分差没几分,但还是值得纪念. 题解: T1 天空龙: 大神题,因为我从不写快读也没有写考场注释的习惯,所以不会做,全hzoi就kx会做,kx真大神级人物. T2 巨神兵: 大神题,一看数据范围这么小,我们考虑状压,最傻逼的暴力思路是压边,但是这显然不行.正解是压点,设$f[s]$为当前选定点集状态为$s$的方案数. 我们考虑转移,当前选定的点集肯定是可以通过边和没有连过来的点相连构成新的方案.所以转移所以我们考虑枚举补集的子集$k$,设$cnt$为s与k

[CSP-S模拟测试96]题解

以后不能再借没改完题的理由不写题解了…… A.求和 求$\sum \sum i+j-1$ 柿子就不化了吧……这年头pj都不考这么弱智的公式化简了…… 坑点1:模数不定,可能没有2的逆元,那么只要先把乘数里的2去掉就好了. 坑点2:1e18炸long long $\rightarrow$ 慢速乘即可 #include<cstdio> #include<iostream> #include<cstring> #include<vector> using name

[CSP-S模拟测试97]题解

A.小盆友的游戏 感觉题解解释的很牵强啊……还是打表找规律比较靠谱 对于每个人,它构造了一个期望函数$f(x)$,设它的跟班个数为$cnt[x]$,那么令$f(x)=2^{cnt[x]}-1$(??鬼知道为什么要等于这个) 然后再定义当前局面的期望函数为每个人期望函数之和. 然后你会发现每次猜拳后局面期望函数变化量都是1 那么期望步数其实就是终止局面期望函数值-初始局面期望函数值 $ans=2^{n-1}-1-\sum (2^{cnt[x]}-1)$ #include<bits/stdc++.h

[CSP-S模拟测试53]题解

A.u 只涉及到区间修改可以考虑差分,然而如果每一行都差分复杂度还是过高.我们发现差分标记也是连续的(一行横着的一行斜着的),所以可以维护两个 差分的差分,扫两遍统计即可. #include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=2005; int read() { int x=0,f=1;char ch=ge

[CSP-S模拟测试60]题解

回去要补一下命运石之门了…… A.嘟嘟噜 给定报数次数的约瑟夫,递推式为$ans=(ans+m)\% i$. 考虑优化,中间很多次$+m$后是不用取模的,这种情况就可以把加法变乘法了.问题在于如何找到下一次需要取模的位置. 解不等式$ans+km \ge i+k$即可,需要处理一下边界. 据说可以证明复杂度是$O(m \log n)$的,但我不是很会. //考场代码 稍丑 #include<bits/stdc++.h> using namespace std; typedef long lon

[CSP-S模拟测试63]题解

A.Median 这题的数据生成方式并没有什么规律,所以可以认为是随机数据. 维护一个桶,表示当前K长区间里的值域情况. 并且用变量记录中位数值域上的左侧有多少个数,当区间调整时一并调整桶和这个变量即可. 由于是随机数据,所以每次的调整幅度并不会很大,近似于常数. 复杂度$O(n)$. #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespa

CSP-S模拟测试69 题解

一如既往的垃圾,又回到了那个场场垫底的自己,明明考场上都想到正解了,但是就是拿不到分,可能是互奶把rp用光了吧以后一定加强训练代码能力. T1: 考场上一直yy矩阵快速幂,虽然自己矩阵快速幂一点都不会还是硬着头皮yy,发现不可做之后并没有及时转化思路,但其实自己预处理的数组就是正解. 切记:不仅矩阵快速幂是log的,普通快速幂也是2333 然后这题其实很水啊,我们设$dp[i][j]$为前$i$列放$j$个棋子的方案数,然后枚举最后一列放多少个棋子就好了. 转移方程为$dp[i][j]=\sum

模拟测试74

T1: 将区间按左端点排序,点排序. 依次扫每一个点,将所有满足条件的区间装入堆中,弹掉所有过期的区间. 每次贪心取右端点最小的区间. 时间复杂度$O(nlogn)$. T2: 把树放在森林中考虑. 设$dp[i][j]$表示$i$个点的森林,有$j$个点在第一棵树的概率. 然后$dp[i][j]=dp[i-1][j-1]*(j-1)*inv[i]+dp[i-1][j]*(i-j)*inv[i]$. 手模一下发现$dp[i][j]=inv[i]$,于是$dp[i][j]$就废掉了...... 设