Noip模拟96 2021.11.12
T1 树上排列
比较水的一道题,不过考场上死活没有想出来使用个区间连乘判断哈希,然后就\(gg\)了
我的应该不容易被卡,因为有四重保证,要卡的话数据必须够早的非常精心,不过我还没有想到卡他的方法
但是同样的,正确性高了代码长度和运行时间都是消耗非常大的。。。。
不建议看我的代码,去看别人的哈希可能打的更快,但是我的基本没哈希冲突
a
#include
#define int long long
using namespace std;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
}using namespace AE86;
const int NN=250005,inf=1e9,mod=998244353;
int T,n,q,w[NN],h[NN];
struct SNOW{int to,next;}e[NN<<1];int head[NN],rp;
auto add=[](int x,int y){
e[++rp]=SNOW{y,head[x]};head[x]=rp;
e[++rp]=SNOW{x,head[y]};head[y]=rp;
};
int dfn[NN],rk[NN],dep[NN],son[NN],top[NN],siz[NN],fa[NN],cnt;
inline void dfs1(int f,int x){
dep[x]=dep[f]+1; siz[x]=1; fa[x]=f;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to; if(y==f) continue;
dfs1(x,y); siz[x]+=siz[y];
if(siz[son[x]]dfn[y])swap(x,y);
return x;
}
struct SNOWtree{
#define lid (id<<1)
#define rid (id<<1|1)
int ll[NN<<2],rr[NN<<2],mn[NN<<2],sm[NN<<2],mx[NN<<2],ml[NN<<2];
inline void pushup(int id){
if(ll[id]==rr[id]) return;
mn[id]=min(mn[lid],mn[rid]);
mx[id]=max(mx[lid],mx[rid]);
sm[id]=sm[lid]+sm[rid];
ml[id]=ml[lid]*ml[rid]%mod;
}
inline void build(int id,int l,int r){
ll[id]=l;rr[id]=r;mn[id]=inf;mx[id]=0;sm[id]=0;ml[id]=1;
if(l==r)return mn[id]=sm[id]=mx[id]=ml[id]=w[rk[l]],void();
int mid=l+r>>1;build(lid,l,mid);build(rid,mid+1,r);pushup(id);
}
inline void update(int id,int pos,int v){
if(ll[id]==rr[id]) return mn[id]=mx[id]=ml[id]=sm[id]=v,void();int mid=ll[id]+rr[id]>>1;
if(pos<=mid) update(lid,pos,v);else update(rid,pos,v);pushup(id);
}
inline int query1(int id,int l,int r){
if(l<=ll[id]&&rr[id]<=r) return sm[id];int mid=ll[id]+rr[id]>>1,ans=0;
if(l<=mid) ans+=query1(lid,l,r);if(r>mid) ans+=query1(rid,l,r);
return ans;
}
inline int query2(int id,int l,int r){
if(l<=ll[id]&&rr[id]<=r) return mx[id];int mid=ll[id]+rr[id]>>1,ans=0;
if(l<=mid) ans=max(ans,query2(lid,l,r));if(r>mid) ans=max(ans,query2(rid,l,r));
return ans;
}
inline int query3(int id,int l,int r){
if(l<=ll[id]&&rr[id]<=r) return mn[id];int mid=ll[id]+rr[id]>>1,ans=inf;
if(l<=mid) ans=min(ans,query3(lid,l,r));if(r>mid) ans=min(ans,query3(rid,l,r));
return ans;
}
inline int query4(int id,int l,int r){
if(l<=ll[id]&&rr[id]<=r) return ml[id];int mid=ll[id]+rr[id]>>1,ans=1;
if(l<=mid) ans=ans*query4(lid,l,r)%mod;if(r>mid) ans=ans*query4(rid,l,r)%mod;
return ans;
}
}tr;
inline int SUM(int x,int y,int ans=0){
while(top[x]!=top[y]){
if(dep[top[x]]dfn[y]) swap(x,y);
ans+=tr.query1(1,dfn[x],dfn[y]);
return ans;
}
inline int MAX(int x,int y,int ans=0){
while(top[x]!=top[y]){
if(dep[top[x]]dfn[y]) swap(x,y);
ans=max(ans,tr.query2(1,dfn[x],dfn[y]));
return ans;
}
inline int MIN(int x,int y,int ans=inf){
while(top[x]!=top[y]){
if(dep[top[x]]dfn[y]) swap(x,y);
ans=min(ans,tr.query3(1,dfn[x],dfn[y]));
return ans;
}
inline int MUL(int x,int y,int ans=1){
while(top[x]!=top[y]){
if(dep[top[x]]dfn[y]) swap(x,y);
ans=ans*tr.query4(1,dfn[x],dfn[y])%mod;
return ans;
}
int opt,x,y;
inline int calc(int n){return n*(n+1)/2;}
auto clear=[](){
memset(head,0,sizeof(head)); rp=0;
memset(son,0,sizeof(son)); cnt=0;
};
namespace WSN{
inline short main(){
wsn=freopen("a.in","r",stdin);
wsn=freopen("a.out","w",stdout);
T=read(); h[0]=1;
for(int i=1;i
T2 连任
线段树分治,没有听说过,于是今天刚学会,模板链接
知道了线段树分治的模板及其思想后我们来看这道题,
不难发现可以把所有的操作进行处理,这样就变成了那个模板题,这样我们把所有的冰茶姬合并操作都挂在线段树上,开一个\(vector\)记录下来
然后进行线段树\(dfs\),过程中维护一下联通块大小,并使用其进行按秩合并,这样保证了复杂度是\(O(nlogm)\)
剩下的就和那一道模板题一样在删边的时候进行冰茶姬撤销即可
b
#include
#define int long long
using namespace std;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
}using namespace AE86;
const int NN=1e5+5,mod=1e9+7;
typedef pair PII;
#define fi first
#define se second
#define mp make_pair
int n,m,inv[NN],ans=1;
struct node{int x,y;}e[NN];
namespace DSU{
int fa[NN],siz[NN],top;
struct Stack{int x,y,szx,szy,tot;}stk[NN];
inline int getfa(int x){return (fa[x]==x)?x:getfa(fa[x]);}
inline void merge(int x,int y){
x=getfa(x); y=getfa(y);
if(x==y) return;
if(siz[x]>siz[y]) swap(x,y);
stk[++top]=(Stack){x,y,siz[x],siz[y],siz[x]+siz[y]};
ans=ans*inv[siz[x]]%mod*inv[siz[y]]%mod;
ans=ans*(siz[x]+siz[y])%mod;
fa[x]=y; siz[y]+=siz[x];
}
inline void back(int lst){
while(top>lst){
Stack now=stk[top--];
ans=ans*inv[now.tot]%mod;
ans=ans*now.szx%mod*now.szy%mod;
fa[now.x]=now.x; siz[now.y]-=now.szx;
}
}
}using namespace DSU;
struct SNOWtree{
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)>>1)
vector tag[NN<<2];
inline void insert(int id,int l,int r,int L,int R,int v){
if(l>R||rhas;
struct update{int l,r,i;};vector upd;
namespace WSN{
inline short main(){
wsn=freopen("b.in","r",stdin);
wsn=freopen("b.out","w",stdout);
n=read();m=read(); inv[0]=inv[1]=1;
for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
for(int i=2;i<=n;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=m;i++){
int opt=read(); e[i].x=read(),e[i].y=read();
if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
if(opt&1) has[mp(e[i].x,e[i].y)]=i;
else{
upd.push_back(update{has[mp(e[i].x,e[i].y)],i-1,has[mp(e[i].x,e[i].y)]});
has.erase(has.find(mp(e[i].x,e[i].y)));
}
}
for(auto x:has)upd.push_back(update{x.se,m,x.se});
for(auto x:upd)tr.insert(1,1,m,x.l,x.r,x.i);
tr.dfs(1,1,m);
return 0;
}
}
signed main(){return WSN::main();}
T3 排列
非常妙的一道二维偏序问题
这题看上去不难想到在\(\forall i,p_i!=-1\)的时候使用\(CDQ\)分治,但是这样复杂度是\(O(nlog^2n)\)只能拿到\(20pts\)
考虑一下如何在\(O(nlogn)\)复杂度内解决这个子任务
为了说明方便,我们设\(a,b,c\)三个数组分别表示下标排列,输入的\(p\),输入的\(q\)
做法很简单,只要使用二维偏序分别处理出\((a,b),(b,c),(a,c)\)的偏序对数之和\(M\),那么答案就是\(\frac{1}{2}(M-C_{n}^{2})\)
考虑一对数对\((i,j)\),要么恰好存在两个数字是偏序关系,要么三个数字都是偏序关系,如果是前者,会恰好计算一次,否则会被统计三次,所以答案就是如上的式子
这样的基础上我们在考虑有\(-1\)的情况,我们定义\(ds\)数组为在\(q\)中没有出现的数字的集合
我们只需要预处理出一个数组\(P[i][0/1]\)分别表示在\(ds\)集合中比\(i\)小的数的个数,比\(i\)大的个数除以集合总个数,不难发现这个东西就是\(-1\)变成比\(i\)小、大的数的概率
然后考虑进行二维偏序,不妨拿\((b,c)\)来举例
考虑所有可能的数对\((i,j)\),在计算第二维偏序的时候只可能出现四种情况
- i=-1,j=k
- i=k,j=-1
- i=-1,j=-1
- i=k,j=kk
先把二元组数组按照\(b\)为第一关键字排序,然后扫的时候记录两个变量\(tmp,big\)分别表示前面的\(-1\)的个数,前面的不是\(-1\)的数比\(-1\)变出来的数小的总期望值,假设当前扫到二元组下标为\(i\)
那么分别考虑其是\(k\)或者\(-1\)的情况
如果是\(k\),那么他前面的所有\(-1\)对其做出的贡献就是\(P[k][0]\times tmp\),不是\(-1\)的直接在树状数组上查询
如果是\(-1\),那么他前面的所有的\(-1\)对其做出的贡献是\(\frac{1}{2}\times tmp\),不是\(-1\)的数做出的总贡献就是\(big\)
c
#include
#define int long long
using namespace std;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
}using namespace AE86;
const int NN=1e6+5,mod=998244353;
inline int qmo(int a,int b,int ans=1){for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;return ans;}
int n,a[NN],b[NN],c[NN],tmp,v2;
int M,tt,ans,no[NN],cnt,P[NN][2];
bool vis[NN];
struct BIT{
int c[NN];
inline void update(int x,int v){while(x0){
big+=P[p[i].y][1];
ans+=tr.query(p[i].y-1)+P[p[i].y][0]*tmp%mod; ans%=mod;
tr.update(p[i].y,1);
}else{
ans+=big+tmp*v2%mod; ans%=mod;
++tmp;
}
}
return ans;
}
namespace WSN{
inline short main(){
wsn=freopen("c.in","r",stdin);
wsn=freopen("c.out","w",stdout);
n=read(); v2=qmo(2,mod-2);
for(int i=1;i<=n;++i)b[i]=read(),a[i]=i;
for(int i=1;i<=n;++i){
c[i]=read();
if(c[i]>0)vis[c[i]]=1;
}for(int i=1;i<=n;++i)if(!vis[i])no[++cnt]=i;
tmp=qmo(cnt,mod-2);
for(int i=1;i<=n;++i){
P[i][0]=(lower_bound(no+1,no+cnt+1,i)-no-1)*tmp%mod;
P[i][1]=(cnt-(upper_bound(no+1,no+cnt+1,i)-no)+1)*tmp%mod;
}
int tmp1=calc(a,b),tmp2=calc(a,c),tmp3=calc(b,c);
M=tmp1+tmp2+tmp3;
tt=n*(n-1)%mod*v2%mod;
ans=(M-tt+mod)%mod*v2%mod;
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T4 追逐
又一个人赢在和他的妹子玩耍了。。。