JZOJ 6275 小L的数列
题目
分析
用矩阵乘法递推 f 1 ∼ f k f1\sim fk f1∼fk的系数,就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);
}