2019.08.07【NOIP提高组】模拟 A 组

JZOJ 6275 小L的数列

题目

在这里插入图片描述


分析

用矩阵乘法递推 f 1 ∼ f k f1\sim fk f1fk的系数,就AC了,时间复杂度 O ( k 3 l o g n ) O(k^3log n) O(k3logn)


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int mod1=998244353,mod2=998244352;
struct maix{int p[211][211];}A,ANS;
int n,m,a[211],ans=1;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline signed mo(int x,int y){return x+y>=mod2?x+y-mod2:x+y;}
inline maix mul(maix A,maix B){
    rr maix C;
    for (rr int i=1;i<=n;++i)
    for (rr int j=1;j<=n;++j) C.p[i][j]=0;
    for (rr int i=1;i<=n;++i)
    for (rr int j=1;j<=n;++j)
    for (rr int k=1;k<=n;++k)
    C.p[i][j]=mo(1ll*A.p[i][k]*B.p[k][j]%mod2,C.p[i][j]);
    return C;
}
inline signed ksm(int x,int y){
	rr int ans=1;
	for (;y;y>>=1,x=1ll*x*x%mod1)
	    if (y&1) ans=1ll*ans*x%mod1;
	return ans;
}
signed main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	m=iut(); n=iut();
	for (rr int i=1;i<=n;++i) A.p[n-i+1][n]=iut();
	for (rr int i=1;i<=n;++i) a[i]=iut(),ANS.p[i][i]=1;
	for (rr int i=1;i<n;++i) A.p[i+1][i]=1;
	if (m<=n) return !printf("%d",a[m]);
	for (m-=n;m;m>>=1,A=mul(A,A))
	    if (m&1) ANS=mul(ANS,A);
	for (rr int i=1;i<=n;++i) ans=1ll*ans*ksm(a[i],ANS.p[i][n])%mod1;  
	return !printf("%d",ans);
}

JZOJ 6274 梦境

题目

m m m个点,匹配 n n n条线段,问最多能匹配多少条线段


分析

点从小到大排序,线段按左端点从小到大排序,用双指针,对于某个点,可以先把左端点不超过该点的线段按照右端点加入小根堆,再把右端点小于该点的线段删除,最后如果小根堆有线段,那么就把堆顶删掉,表示选择该线段与该点匹配,贪心正确性显然,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
struct rec{
    int l,r;
    bool operator <(const rec &t)const{
	    return l!=t.l?l<t.l:r<t.r;
	}
}a[200001];
int n,m,b[200001],ans,cnt,heap[200001];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void Push(int w){
    heap[++cnt]=w;
    rr int x=cnt;
    while (x>1){
        if (heap[x>>1]>heap[x])
            swap(heap[x>>1],heap[x]),x>>=1;
        else return;
    }
}
inline void Pop(){
    heap[1]=heap[cnt--];
    rr int x=1;
    while ((x<<1)<=cnt){
        rr int y=x<<1;
        if (y<cnt&&heap[y+1]<heap[y]) ++y;
        if (heap[y]<heap[x]) swap(heap[y],heap[x]),x=y;
            else return;
    }
}
signed main(){
	freopen("dream.in","r",stdin);
	freopen("dream.out","w",stdout);
	n=iut(); m=iut();
	for (rr int i=1;i<=n;++i) a[i]=(rec){iut(),iut()};
	for (rr int i=1;i<=m;++i) b[i]=iut();
	sort(a+1,a+1+n),sort(b+1,b+1+n);
	for (rr int i=1,j=1;i<=m;++i){
		while (a[j].l<=b[i]&&j<=n) Push(a[j++].r);
		while (cnt&&heap[1]<b[i]) Pop();
		if (cnt) ++ans,Pop();
	}
	return !printf("%d",ans);
} 

JZOJ 6276 树 JZOJ 100019 A

题目

有一棵 n n n个节点的无根树,给出其中的 m m m对点对< x , y x,y x,y>。问有多少条树上的简单路径< u , v u,v u,v>满足该路径上不存在任何一对给出的点对< x , y x,y x,y>。这里我们认为路径< u , v u,v u,v>和< v , u v,u v,u>是相同的。并且对于题目中给出的点对< x , y x,y x,y>满足 x ! = y x!=y x!=y,对于你要计数的路径< u , v u,v u,v>满足 u ! = v u!=v u!=v(即单点不算答案)。(可以认为第二个是 n l o g n nlogn nlogn条限制)


分析

那可以用容斥,用所有点对减去不合法的,对于某个限制< x , y x,y x,y>,如果它们在不同的子树中,那么其实答案就是两个子树大小的乘积,如果它们为祖先关系,若 x x x的深度小于 y y y的深度,那么显然就是非 x x x y y y路径上 x x x的子节点子树的大小与以 y y y为子树的大小乘积,所以这道题就写完了
???重复怎么办,我们还要用dfs序,配合扫描线和线段树解决此题


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
#define rr register
using namespace std;
const int N=100011; struct node{int y,next;}e[N<<1]; long long ans;
struct rec{
	int x,l,r,w;
	bool operator <(const rec &t)const{return x<t.x;}
}a[N<<2];
int n,m,tot,k=1,dfn[N],ano[N],ls[N],dep[N],f[N][18],bas,cnt,lazy[N<<2],w[N<<2];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void dfs(int x,int fa){
	dfn[x]=++cnt,dep[x]=dep[fa]+1,f[x][0]=fa;
	for (rr int i=ls[x];i;i=e[i].next)
	    if (e[i].y!=fa) dfs(e[i].y,x);
	ano[x]=cnt;
}
inline signed lca(int x,int y){
	for (rr int i=bas;~i;--i)
	if (dep[f[y][i]]>dep[x]) y=f[y][i];
	return y; 
}
inline void add(int x1,int x2,int y1,int y2){
	a[++tot]=(rec){x1,y1,y2,1},a[++tot]=(rec){x2+1,y1,y2,-1};
}
inline void update(int k,int l,int r,int x,int y,int end){
	if (l==x&&r==y){
		lazy[k]+=end;
		if (lazy[k]) w[k]=r-l+1;
		    else if (l==r) w[k]=0;
                else w[k]=w[k<<1]+w[k<<1|1];
		return;
	}
	rr int mid=(l+r)>>1;
	if (y<=mid) update(k<<1,l,mid,x,y,end);
	else if (x>mid) update(k<<1|1,mid+1,r,x,y,end);
	else update(k<<1,l,mid,x,mid,end),update(k<<1|1,mid+1,r,mid+1,y,end);
	if (lazy[k]) w[k]=r-l+1;
        else w[k]=w[k<<1]+w[k<<1|1];
}
signed main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=iut(); m=iut();
	for (rr int i=1;i<n;++i){
		rr int x=iut(),y=iut();
		e[++k]=(node){y,ls[x]},ls[x]=k;
		e[++k]=(node){x,ls[y]},ls[y]=k;
	}
	dfs(1,0); ans=1ll*n*(n-1)>>1;
	bas=log(n)/log(2)+1;
	for (rr int j=1;j<=bas;++j)
	for (rr int i=1;i<=n;++i)
	    f[i][j]=f[f[i][j-1]][j-1];
	for (rr int i=1;i<=m;++i){
		rr int x=iut(),y=iut();
		if (dfn[x]>dfn[y]) x^=y,y^=x,x^=y;
		if (dfn[y]<=ano[x]){
			rr int son=lca(x,y);
			if (dfn[son]>1) add(1,dfn[son]-1,dfn[y],ano[y]);
			if (ano[son]<n) add(dfn[y],ano[y],ano[son]+1,n);
		}else add(dfn[x],ano[x],dfn[y],ano[y]);
	}
	sort(a+1,a+1+tot);
	for (rr int i=1;i<tot;++i){
		update(1,1,n,a[i].l,a[i].r,a[i].w);
		ans-=w[1]*(a[i+1].x-a[i].x);
	}
	return !printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值