51nod一日游
忽然意识到昨天交了十几发的CE是因为没选择语言qwq
本来准备按着题目分类刷一遍的,看来是刷不完了
1618树或非树
树链剖分
思路还是挺简单的吧?
把环拎出来单独处理(n条边的联通图存在且仅存在一个环),那么若不考虑环
图中的连通图个数=总点数-"开"的边数
若这条环联通了,那么实际上有一条边并没有是减少联通块个数,但答案却已经减去了,所以ans++
边的开合用树链剖分维护即可
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc();x=0;
for(;!isdigit(c);c=nc());
for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
}
const int N=300002;
bool vis[N];
int n,m,head[N],nex[N<<1],to[N<<1],cnt,q[N],tot,c[N],c1,inc[N],bl[N];
struct data{int s,tag;}t[N<<3];
inline void addedge(int u,int v){
nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v;
}
inline void getcyc(int x,int fa){
vis[x]=1,q[++tot]=x;
for(int i=head[x];i;i=nex[i])
if(!vis[to[i]])getcyc(to[i],x);
else if(to[i]!=fa){
if(c1)return;
for(int y=0;y!=to[i];)y=q[tot--],c[++c1]=y,inc[y]=c1;
}tot--;
}
int siz[N],dep[N],fa[N],son[N],top[N],pos[N],sz;
inline void dfs1(int x){
siz[x]=1,dep[x]=dep[fa[x]]+1;
for(int i=head[x];i;i=nex[i]){
if(to[i]==fa[x]||inc[to[i]])continue;
fa[to[i]]=x,bl[to[i]]=bl[x],dfs1(to[i]),siz[x]+=siz[to[i]],son[x]=siz[to[i]]>siz[son[x]]?to[i]:son[x];
}
}
inline void dfs2(int x,int topf){
pos[x]=++sz,top[x]=topf;
if(!son[x])return;
dfs2(son[x],topf);
for(int i=head[x];i;i=nex[i])if(to[i]!=fa[x]&&!inc[to[i]]&&to[i]!=son[x])dfs2(to[i],to[i]);
}
inline void pushdown(int x,int l,int r){
if(l==r||!t[x].tag)return;
t[x].tag=0;int mid=l+r>>1;
t[x<<1].tag^=1,t[x<<1|1].tag^=1;
t[x<<1].s=mid-l+1-t[x<<1].s,t[x<<1|1].s=r-mid-t[x<<1|1].s;
}
inline void updata(int x,int l,int r,int L,int R){
if(L>R)return;
if(l==L&&r==R){
t[x].tag^=1,t[x].s=r-l+1-t[x].s;return;
}
pushdown(x,l,r);int mid=l+r>>1;
updata(x<<1,l,mid,L,min(R,mid)),updata(x<<1|1,mid+1,r,max(L,mid+1),R);
t[x].s=t[x<<1].s+t[x<<1|1].s;
}
inline int query(int x,int l,int r,int L,int R){
if(L>R)return 0;
if(l==L&&r==R)return t[x].s;
pushdown(x,l,r);int mid=l+r>>1;
return query(x<<1,l,mid,L,min(R,mid))+query(x<<1|1,mid+1,r,max(L,mid+1),R);
}
inline void upd(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
updata(1,1,n+c1,pos[top[x]],pos[x]),x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
updata(1,1,n+c1,pos[y]+1,pos[x]);
}
int main(){
read(n),read(m);int x,y,px,py;
for(int u,v,i=1;i<=n;i++)read(u),read(v),addedge(u,v),addedge(v,u);
getcyc(1,0);
for(int i=1;i<=c1;i++)bl[c[i]]=c[i],dfs1(c[i]),dfs2(c[i],c[i]);
while(m--){
read(x),read(y);
if(bl[x]==bl[y])upd(x,y);
else{
upd(x,bl[x]),upd(y,bl[y]),x=bl[x],y=bl[y],px=inc[x],py=inc[y];
if(px<py){
if(py-px<c1-py+px||py-px==c1-py+px&&c[px+1]<c[px==1?c1:px-1])updata(1,1,n+c1,px+1+n,py+n);
else updata(1,1,n+c1,1+n,px+n),updata(1,1,n+c1,py+1+n,c1+n);
}else{
if(px-py<c1-px+py||px-py==c1-px+py&&c[px-1]<c[px%c1+1])updata(1,1,n+c1,py+1+n,px+n);
else updata(1,1,n+c1,1+n,py+n),updata(1,1,n+c1,px+1+n,c1+n);
}
}
int ans=n-t[1].s;
if(query(1,1,n+c1,n+1,n+c1)==c1)ans++;
printf("%d\n",ans);
}
return 0;
}
1742开心的小Q
莫比乌斯
果然数学差得已经不成样了,稍微复习了下
做不来——
留坑
1636教育改革
DP
比较愉快了
1789跑得比谁都快
斜率优化
很容易发现单调性,由此得到斜率优化;
在树上的话是多条链同时进行的,刚开始想用可持久化数组,可是不是特别好
想想看,记个前驱,后缀就能维护head,tail了,且一个数只会最多进队一次,O(n)的时间复杂度
然后——
1e18会爆炸啊,不想写高精度
那就改代码了...rp++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
struct data{int pre,now,sub;}Z[N];
int nex[N],to[N],head[N],cnt;
int num,L,R,MX;
ll fa,n,p,C[N],ans,f[N],co[N];
void read(ll &x){
char c=getchar();x=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
}
inline ll calc(int x,int i){return f[x]+C[i-x]+co[x];}
void ins(int u,int v){nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v;}
int divi(int L,int R){
int l=L+1,r=n;
while(l<=r){
int mid=l+r>>1;
if(f[L]+C[mid-L]+co[L]>=f[R]+C[mid-R]+co[R])r=mid-1;else l=mid+1;
}
return r;
}
void dfs(int x){
int l=L,r=R;
for(;Z[L].sub&&calc(Z[L].now,x)>=calc(Z[Z[L].sub].now,x);L=Z[L].sub);
f[x]=(x-Z[L].now<MX)?calc(Z[L].now,x):1e18;
ans=(!head[x]&&ans>f[x])?f[x]:ans;
for(;Z[R].pre&&divi(Z[Z[R].pre].now,Z[R].now)>=divi(Z[R].now,x);R=Z[R].pre);
int last=Z[R].sub,nowR=R;
Z[++num]=(data){R,x,0},Z[R].sub=num,R=num;
for(int i=head[x];i;i=nex[i])dfs(to[i]);
Z[nowR].sub=last,L=l,R=r;
}
int main(){
read(n),read(p);
for(int i=1;i<=n;i++){
C[i]=1,MX=i;
for(int j=1;j<=p;j++){
C[i]=1ll*C[i]*i;
if(C[i]>1e18)break;
}
if(C[i]>1e18)break;
}
MX=(MX==n&&C[n]<=1e18)?n+1:MX;
ans=1e18;
for(int i=1;i<=n;i++)read(co[i]),read(fa),ins(fa,i);
L=R=num=1;Z[1]=(data){0,1,0};
for(int i=head[1];i;i=nex[i])dfs(to[i]);
return printf("%lld\n",ans),0;
}
1623完美消除
数位DP
维护一个单调队列(0-9)来维护消除次数
#include<cstdio>
#include<cstring>
typedef long long ll;
ll L,R,f[20][1<<10][20];
int k,a[20],t[11];
bool vis[20][1<<10][20];
inline int cal(int p,int i){return p=p&(t[i+1]-1);}
inline ll dfs(int pos,int s,int tt,bool lim){
if(pos<0)return tt==k;
if(!lim&&f[pos][s][tt]!=-1)return f[pos][s][tt];
int mx=lim?a[pos]:9;ll res=0;
for(int i=0;i<=mx;i++){
if(s&t[i]||i==0)res+=dfs(pos-1,cal(s,i),tt,lim&&i==a[pos]);
else res+=dfs(pos-1,cal(s|t[i],i),tt+1,lim&&i==a[pos]);
}
if(!lim)f[pos][s][tt]=res;
return res;
}
inline ll solve(ll x){
memset(f,-1,sizeof f);
int pos=0;for(;x;a[pos++]=x%10,x/=10);
return dfs(pos-1,0,0,1);
}
int main(){
t[0]=1;for(int i=1;i<=10;i++)t[i]=t[i-1]<<1;
scanf("%lld%lld%d",&L,&R,&k);
return printf("%lld\n",solve(R)-solve(L-1)),0;
}
1486大大走格子
容斥,组合数
以前最年长的学长讲过
是个好题
用了组合数&容斥
如果每次只能向下或向右走
那么从(x1,y1)走到(x2,y2) [x1<=x2,y1<=y2]的方案数是C(x2-x1+y2-y1,x2-x1)
总方案数=C(h+w-2,h-1)-经过不能经过的位置的方案数
关键是怎样计算经过不能经过的位置的方案
现将点按x,y坐标排序
f[i]表示从左上角走过来经过的第一个不能经过位置是第i个点的方案数
则f[i]=C(a[i].x+a[i].y-2,a[i].x-1)-sigma(f[j]*C(a[i].x+a[i].y-a[j].x-a[j].y,a[i].x-a[j].x))[j<i&&a[j].x<=a[i].x&&a[j].y<=a[i].y]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2002,M=100002,mo=1e9+7;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc();x=0;
for(;!isdigit(c);c=nc());
for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
}
struct data{
int x,y;
bool operator<(const data &b)const{return x==b.x?y<b.y:x<b.x;}
}a[N];
int w,h,n;
ll ans,c[M<<1],f[N],inv[M<<1],cinv[M<<1];
ll C(int x,int y){return 1ll*c[x]*cinv[y]%mo*cinv[x-y]%mo;}
int main(){
read(h),read(w),read(n);
c[0]=inv[0]=c[1]=inv[1]=cinv[0]=cinv[1]=1;for(int i=2;i<=w+h;++i)c[i]=1ll*c[i-1]*i%mo,inv[i]=1ll*(mo-(mo/i))*inv[mo%i]%mo,cinv[i]=1ll*cinv[i-1]*inv[i]%mo;
for(int i=1;i<=n;++i)read(a[i].x),read(a[i].y);
sort(a+1,a+1+n);
for(int i=1;i<=n;++i){
f[i]=C(a[i].x+a[i].y-2,a[i].x-1);
for(int j=1;j<i;j++)if(a[i].y>=a[j].y)f[i]=(f[i]-f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mo)%mo;
ans=(ans+f[i]*C(h-a[i].x+w-a[i].y,h-a[i].x)%mo)%mo;
}
printf("%lld\n",((C(h+w-2,h-1)-ans)%mo+mo)%mo);
return 0;
}
1553周期串查询
hash和线段树
如果[l,r]周期为d
则[l,r-d]与[l+d,r]的hash值相同
1672区间交
先按照左端点排序,再用个树状数组,二分查找最右端的被覆盖至少k 次的点
1766树上的最远点对
线段树
一点都不会求求直径,也算填坑
我们知道,树上最远的距离是树的直径。
然后,直径可以由两个点集中的直径的总共四个端点两两配对得到。
于是我们就可以用线段树来维护这个东西。
注意求距离要用欧拉序列,不能用倍增,否则会爆炸性超时。
-alan_cty
简单证明一下。
我们看作是 x 所在的连通块通过边(x, y)连向 y 所在的连通块。
若新直径不经过(x, y),则就是原来的两条直径取 max。
若新直径经过(x, y),就要考虑 x 延伸到哪儿、y 延伸到哪儿了。由直径的定义可知,x 能走到的最远点之一是 x 所在连通块直径的端点,y 同理。因此这时新直径的两个端点都是旧直径的端点。
(这个证明也适用于增量法求树的直径,即我给一棵树加一个新点,那么新直径必有一端点是旧直径的端点)
(注意只能是树,普通图没有这些性质)
-rzO_KQP_Orz
#include<bits/stdc++.h>
using namespace std;
const int N=100002;
struct note{int a,b;}tr[N*5];
int n,m,k,tot,d[N],dfn[N<<1],fir[N],sum[N],f[N<<1][18];
int to[N<<1],nex[N<<1],w[N<<1],head[N],mi[18],cnt;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc();x=0;
for(;!isdigit(c);c=nc());
for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
}
void add(int u,int v,int wi){
nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v,w[cnt]=wi;
}
void dfs(int x,int y){
d[x]=d[y]+1,dfn[++tot]=x,fir[x]=tot;
for(int i=head[x];i;i=nex[i])if(to[i]!=y)sum[to[i]]=sum[x]+w[i],dfs(to[i],x),dfn[++tot]=x;
}
int lca(int x,int y){
x=fir[x],y=fir[y];
if(x>y)swap(x,y);
int z=log2(y-x+1);
if(d[dfn[f[x][z]]]<d[dfn[f[y-mi[z]+1][z]]])return dfn[f[x][z]];else return dfn[f[y-mi[z]+1][z]];
}
inline int len(int x,int y){
int z=lca(x,y);return sum[x]+sum[y]-2*sum[z];
}
note merge(note y,note z,int bz){
note x;int mx=0,l;
if(!bz){
l=len(y.a,y.b);if(l>mx)mx=l,x.a=y.a,x.b=y.b;
l=len(z.a,z.b);if(l>mx)mx=l,x.a=z.a,x.b=z.b;
}
l=len(y.a,z.a);if(l>mx)mx=l,x.a=y.a,x.b=z.a;
l=len(y.a,z.b);if(l>mx)mx=l,x.a=y.a,x.b=z.b;
l=len(y.b,z.a);if(l>mx)mx=l,x.a=y.b,x.b=z.a;
l=len(y.b,z.b);if(l>mx)mx=l,x.a=y.b,x.b=z.b;
return x;
}
void build(int v,int l,int r){
if(l==r){tr[v].a=tr[v].b=l;return;}
int mid=(l+r)/2;
build(v<<1,l,mid),build(v<<1|1,mid+1,r);
tr[v]=merge(tr[v<<1],tr[v<<1|1],0);
}
note find(int v,int l,int r,int x,int y){
if(l==x&&r==y)return tr[v];
int mid=l+r>>1;
if(y<=mid)return find(v<<1,l,mid,x,y);
else if(x>mid)return find(v<<1|1,mid+1,r,x,y);
else return merge(find(v<<1,l,mid,x,mid),find(v<<1|1,mid+1,r,mid+1,y),0);
}
int main(){
freopen("t.in","r",stdin);
read(n);
for(int u,v,wi,i=1;i<n;i++)read(u),read(v),read(wi),add(u,v,wi),add(v,u,wi);
dfs(1,0),mi[0]=1;for(int i=1;i<18;i++)mi[i]=mi[i-1]<<1;
for(int i=1;i<=tot;i++)f[i][0]=i;
for(int j=1;j<=log2(tot);j++)
for(int i=1;i<=tot-mi[j]+1;i++)
if(d[dfn[f[i][j-1]]]<d[dfn[f[i+mi[j-1]][j-1]]])f[i][j]=f[i][j-1];else f[i][j]=f[i+mi[j-1]][j-1];
build(1,1,n);
int x,y,z,k;
for(read(m);m;m--){
read(x),read(y),read(z),read(k);
note tmp=merge(find(1,1,n,x,y),find(1,1,n,z,k),1);
printf("%d\n",len(tmp.a,tmp.b));
}
return 0;
}