noip模拟83
今天的4个题有3个数据是错的,就nm离谱,冲了T1的正解,T2部分分少取了个模数少了30。。。
最后100+30+30+0
T1
两种做法,一种是直接维护区间加和以及区间乘机%大质数,另一种直接维护多重集hash,数剖实现一下就行了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=2.5e5+5;
struct edge{int to,nxt;}e[maxn<<1];int a[maxn],val[maxn];
int head[maxn],cnt,num,fa[maxn],siz[maxn],top[maxn],son[maxn],dfn[maxn],dep[maxn],n,m;
int jc[maxn],jc2[maxn],q;
inline void add(int x,int y)
{
e[++num]=(edge){y,head[x]};head[x]=num;
e[++num]=(edge){x,head[y]};head[y]=num;
}
inline void dfs1(int x,int f)
{
siz[x]=1;son[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==f) continue;
dep[y]=dep[x]+1;fa[y]=x;
dfs1(y,x);siz[x]+=siz[y];
if(siz[y]>=siz[son[x]]) son[x]=y;
}
}
inline void dfs2(int x,int tp)
{
dfn[x]=++cnt;top[x]=tp;val[cnt]=a[x];
if(!son[x])return; dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline void clear()
{
num=0;cnt=0;
memset(head,0,sizeof(head));
}
const int mod=998244353;
const int mod2=1e9+7;
struct seg_tree{
#define lid id<<1
#define rid id<<1|1
struct tree{int mx,mn;ll sum;int cj,cj2;}tr[maxn<<2];
inline tree add(tree a,tree b)
{
a.mn=min(a.mn,b.mn);
a.mx=max(a.mx,b.mx);
a.sum=a.sum+b.sum;
a.cj=1ll*a.cj*b.cj%mod;
a.cj2=1ll*a.cj2*b.cj2%mod2;
return a;
}
inline void build(int id,int l,int r)
{
if(l==r) return tr[id]=(tree){val[l],val[l],val[l],val[l],val[l]},void();
int mid=(l+r)>>1; build(lid,l,mid);build(rid,mid+1,r);
tr[id]=add(tr[lid],tr[rid]);
}
inline void update(int id,int l,int r,int pos,int val)
{
if(l==r)return tr[id]=(tree){val,val,val,val,val},void();
int mid=(l+r)>>1;
if(pos<=mid) update(lid,l,mid,pos,val);
else update(rid,mid+1,r,pos,val);
tr[id]=add(tr[lid],tr[rid]);
}
inline tree query(int id,int l,int r,int L,int R)
{
if(l>=L&&r<=R) return tr[id];
int mid=(l+r)>>1;tree ans=(tree){0,1000000000,0,1,1};
if(L<=mid) ans=add(ans,query(lid,l,mid,L,R));
if(R>mid) ans=add(ans,query(rid,mid+1,r,L,R));
return ans;
}
inline bool check(int x,int y)
{
tree ans=(tree){0,1000000000,0,1,1};
if(!x||!y){return false;}
int nw=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans=add(ans,query(1,1,n,dfn[top[x]],dfn[x]));
nw+=dfn[x]-dfn[top[x]]+1;x=fa[top[x]];
}
if(dep[x]<dep[y]) ans=add(ans,query(1,1,n,dfn[x],dfn[y])),nw+=dfn[y]-dfn[x]+1;
else ans=add(ans,query(1,1,n,dfn[y],dfn[x])),nw+=dfn[x]-dfn[y]+1;
if(ans.mx!=nw) return false; if(ans.mn!=1) return false;
if(ans.sum!=1ll*(nw)*(nw+1)/2)return false;
if(ans.cj!=jc[nw]) return false;
if(ans.cj2!=jc2[nw]) return false;
return true;
}
}T;
signed main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int t=read();jc[0]=jc2[0]=1;
for(int i=1;i<=maxn-1;i++)
{
jc[i]=1ll*jc[i-1]*i%mod;
jc2[i]=1ll*jc2[i-1]*i%mod2;
}
while(t--)
{
clear();n=read();q=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++) add(read(),read());
dfs1(1,0);dfs2(1,1);T.build(1,1,n);
while(q--)
{
int type=read();
if(type==1)
{
int x=read(),y=read();
if(T.check(x,y)) puts("Yes");
else puts("No");
}
else
{
int x=read(),y=read();
T.update(1,1,n,dfn[x],y);
}
}
}
}
T2 连任
一个比较经典的线段树分治模板,在线段树上维护一个可撤销并查集,线段树建立在时间轴上,然后一遍dfs就好了
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=1e5+5,mod=1e9+7;
int fa[maxn],siz[maxn],m,n,ans=1,inv[maxn];
inline int ksm(int x,int y)
{
int res=1;x=x%mod;
for(;y;y>>=1){if(y&1)res=res*x%mod;x=x*x%mod;}
return res;
}
map<int,int>mp[maxn];
struct data{int x,y,l,r;}p[maxn];
struct node{int x,siz;};
stack<node>stk;
inline int getfa(int x){while(fa[x]!=x) x=fa[x];return x;}
inline void merge(int x,int y)
{
int fx=getfa(x),fy=getfa(y);
if(fx==fy) return ;
if(siz[fx]>siz[fy]) swap(fx,fy);
stk.push((node){fx,siz[fx]});
ans=ans*inv[siz[fx]]%mod*inv[siz[fy]]%mod*(siz[fx]+siz[fy])%mod;
siz[fy]=siz[fy]+siz[fx];fa[fx]=fy;
}
inline void del(int sze)
{
while(stk.size()>sze)
{
node a=stk.top();
ans=ans*inv[siz[fa[a.x]]]%mod*a.siz%mod*(siz[fa[a.x]]-a.siz)%mod;
siz[fa[a.x]]-=a.siz; fa[a.x]=a.x; stk.pop();
}
}
vector<int>vec[maxn<<2];
inline void insert(int id,int l,int r,int ll,int rr,int x)
{
if(l>=ll&&r<=rr)
{
vec[id].push_back(x);
return ;
}
int mid=(l+r)>>1;
if(ll<=mid) insert(lid,l,mid,ll,rr,x);
if(rr>mid) insert(rid,mid+1,r,ll,rr,x);
}
inline void dfs(int id,int l,int r)
{
int nw=stk.size();
for(auto y:vec[id]) merge(p[y].x,p[y].y);
if(l==r){printf("%lld\n",ans);del(nw);return;}
int mid=(l+r)>>1;
dfs(lid,l,mid);dfs(rid,mid+1,r);
del(nw);
}
signed main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
for(int i=1;i<=n;i++) inv[i]=ksm(i,mod-2);
for(int i=1;i<=m;i++)
{
int type=read(),x=read(),y=read();
if(type==1)
{
p[i].x=x;p[i].y=y; p[i].l=i;
mp[x][y]=mp[y][x]=i;p[i].r=m;
}
else
{
int id=mp[x][y]; p[id].r=i-1;
}
}
for(int i=1;i<=m;i++) if(p[i].x)
insert(1,1,m,p[i].l,p[i].r,i);
dfs(1,1,m);
}
T3 排列
NB题,学会了处理纯三维偏序统计的\(O(nlog(n))\)做法
考虑分别对其中的两维处理二维偏序,那么如果其中两个点是三维偏序,那么肯定会被计算三次,如果只有两维满足偏序就只会被计算一次
那么我们设这三次处理的二维偏序的和为\(M\),那么三维偏序的个数为\((M-n*(n-1)/2)/2\)
我觉得这是比较显然的,就是两个点肯定满足不是二维偏序就是三维偏序
那么减去所有的数对就相当于减去了只有二维和三维的三分之一
有-1的计算一下贡献就行了
#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=1e6+5;
const int mod=998244353;
struct node{int a,b,c;}p[maxn];
int n,a[maxn],b[maxn],vis[maxn];
struct tree{
#define lowbit(x) (x&(-x))
int c[maxn];
inline void update(int x,int val){for(;x<=n;x+=lowbit(x))c[x]+=val;}
inline int query(int x)
{int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
inline void clear(){memset(c,0,sizeof(c));}
}T,T2;
inline int ksm(int x,int y)
{
int res=1;x=x%mod;
for(;y;y>>=1){if(y&1)res=1ll*res*x%mod;x=1ll*x*x%mod;}
return res;
}
bool cmp2(node a,node b){return a.b<b.b;}
signed main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
n=read();int all=0;
for(int i=1;i<=n;i++)a[i]=read(),p[i].a=i,p[i].b=a[i];
for(int i=1;i<=n;i++)
{
b[i]=read(),p[i].c=b[i];
if(b[i]==-1) all++;
}
for(int i=1;i<=n;i++)if(b[i]!=-1) vis[b[i]]=1;
for(int i=1;i<=n;i++) if(!vis[i]) vis[i]=1;else vis[i]=0;
for(int i=1;i<=n;i++) vis[i]=vis[i-1]+vis[i];
int ans=0;
for(int i=1;i<=n;i++)
{
ans+=T.query(p[i].b);
T.update(p[i].b,1);
}
T.clear();
int iv1=ksm(vis[n],mod-2),iv2=ksm(vis[n]-1,mod-2),sum=0;
for(int i=1;i<=n;i++) if(vis[i]-vis[i-1])
(sum+=iv1*iv2%mod*vis[i-1]%mod)%=mod;
for(int i=1;i<=n;i++)
if(p[i].c!=-1)
{
ans+=T.query(p[i].c); T.update(p[i].c,1);
int tmp=T2.query(n),tmp2=vis[p[i].c]; (ans+=tmp*tmp2%mod*iv1)%=mod;
tmp=all-tmp; tmp2=vis[n]-vis[p[i].c]; (ans+=tmp*tmp2%mod*iv1)%=mod;
}
else {int tmp=T2.query(n); ans=(ans+tmp*sum%mod)%mod; T2.update(1,1);}
T.clear();T2.clear(); sort(p+1,p+1+n,cmp2);
for(int i=1;i<=n;i++)
if(p[i].c!=-1)
{
ans+=T.query(p[i].c); T.update(p[i].c,1);
int tmp=T2.query(n),tmp2=vis[p[i].c]; (ans+=tmp*tmp2%mod*iv1)%=mod;
tmp=all-tmp; tmp2=vis[n]-vis[p[i].c]; (ans+=tmp*tmp2%mod*iv1)%=mod;
}
else {int tmp=T2.query(n);ans=(ans+tmp*sum%mod)%mod;T2.update(1,1);}
ans=(ans-((n*(n-1)/2)%mod)+mod)*ksm(2,mod-2)%mod;
printf("%lld\n",ans%mod);
}
T4 追逐
先咕了。。。