首页 > 其他分享 >NOI2021 题解

NOI2021 题解

时间:2023-07-13 09:47:06浏览次数:38  
标签:int 题解 sum tree NOI2021 lson include rson

[NOI2021] 轻重边

转化一下题意:每次给一条链染色,查一条链从 \(x\) 到 \(y\) 有几条边两端颜色相同。那这个随便树剖线段树就好了。也可以 LCT,码量可能要小点。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
int n,m;
struct gra{
    int v,next;
}edge[200010];
int t,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int num,dfn[100010],rk[100010],son[100010],size[100010],fa[100010],top[100010],dep[100010];
void dfs1(int x,int f){
    size[x]=1;dep[x]=dep[f]+1;fa[x]=f;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs1(edge[i].v,x);
            size[x]+=size[edge[i].v];
            if(size[son[x]]<size[edge[i].v])son[x]=edge[i].v;
        }
    }
}
void dfs2(int x,int f,int tp){
    dfn[x]=++num;rk[num]=x;top[x]=tp;
    if(son[x])dfs2(son[x],x,tp);
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f&&edge[i].v!=son[x])dfs2(edge[i].v,x,edge[i].v);
    }
}
struct data{
    int l,r,sum;
    friend data operator+(data s,data t){
        if(s.l==0&&s.r==0)return t;
        if(t.l==0&&t.r==0)return s;
        data ans;
        ans.l=s.l,ans.r=t.r;
        ans.sum=s.sum+t.sum+(s.r==t.l);
        return ans;
    }
};
struct node{
    #define lson rt<<1
    #define rson rt<<1|1
    data val;
    int len,lz;
}tree[400010];
void pushup(int rt){
    tree[rt].val=tree[lson].val+tree[rson].val;
}
void pushtag(int rt,int val){
    tree[rt].val.l=tree[rt].val.r=val;tree[rt].val.sum=tree[rt].len-1;tree[rt].lz=val;
}
void pushdown(int rt){
    if(tree[rt].lz){
        pushtag(lson,tree[rt].lz);
        pushtag(rson,tree[rt].lz);
        tree[rt].lz=0;
    }
}
void build(int rt,int l,int r){
    tree[rt].len=r-l+1;
    tree[rt].val={0,0,0};tree[rt].lz=0;
    if(l==r){
        tree[rt].val={l,l,0};return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    pushup(rt);
}
void update(int rt,int L,int R,int l,int r,int val){
    if(l<=L&&R<=r){
        pushtag(rt,val);return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)update(lson,L,mid,l,r,val);
    if(mid<r)update(rson,mid+1,R,l,r,val);
    pushup(rt);
}
data query(int rt,int L,int R,int l,int r){
    if(l<=L&&R<=r)return tree[rt].val;
    pushdown(rt);
    int mid=(L+R)>>1;data val={0,0,0};
    if(l<=mid)val=val+query(lson,L,mid,l,r);
    if(mid<r)val=val+query(rson,mid+1,R,l,r);
    return val;
}
void change(int x,int y,int val){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        update(1,1,n,dfn[top[x]],dfn[x],val);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    update(1,1,n,dfn[x],dfn[y],val);
}
int query(int x,int y){
    data L={0,0,0},R={0,0,0};
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]]){
            data ret=query(1,1,n,dfn[top[x]],dfn[x]);
            L=ret+L;x=fa[top[x]];
        }
        else{
            data ret=query(1,1,n,dfn[top[y]],dfn[y]);
            R=ret+R;y=fa[top[y]];
        }
    }
    if(dep[x]<dep[y]){
        swap(L.l,L.r);
        data ret=query(1,1,n,dfn[x],dfn[y]);
        L=L+ret+R;
        return L.sum;
    }
    else{
        swap(R.l,R.r);
        data ret=query(1,1,n,dfn[y],dfn[x]);
        R=R+ret+L;
        return R.sum;
    }
}
int main(){
    int tim;scanf("%d",&tim);
    while(tim--){
        scanf("%d%d",&n,&m);t=num=0;
        for(int i=1;i<n;i++){
            int u,v;scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        dfs1(1,0);dfs2(1,0,1);
        build(1,1,n);
        int cnt=n;
        while(m--){
            int od,u,v;scanf("%d%d%d",&od,&u,&v);
            if(od==1){
                cnt++;change(u,v,cnt);
            }
            else printf("%d\n",query(u,v));
        }
        for(int i=1;i<=n;i++)head[i]=son[i]=0;
    }
    return 0;
}

[NOI2021] 路径交点

LGV 板子。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int mod=998244353;
int n,k,a[110][110],sum[110],m[110];
struct node{
    int v,next;
}edge[4000010];
int t,head[20010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int cnt[20010];
bool vis[20010];
void bfs(int st){
    queue<int>q;
    for(int i=1;i<=sum[k];i++)cnt[i]=0,vis[i]=false;
    q.push(st);cnt[st]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=edge[i].next){
            cnt[edge[i].v]=cnt[edge[i].v]+cnt[x];
            if(cnt[edge[i].v]>=mod)cnt[edge[i].v]-=mod;
            if(!vis[edge[i].v])q.push(edge[i].v),vis[edge[i].v]=true;
        }
    }
}
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=1ll*ans*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return ans;
}
int gauss(int n){
    int ans=1,w=1;
    for(int i=1;i<=n;i++){
        int mx=i;
        for(int j=i+1;j<=n;j++){
            if(a[j][i]>a[mx][i])mx=j;
        }
        if(mx!=i){
            w=-w;
            for(int j=1;j<=n;j++)swap(a[i][j],a[mx][j]);
        }
        if(!a[i][i])return 0;
        int inv=qpow(a[i][i],mod-2);
        for(int j=1;j<=n;j++){
            if(i==j)continue;
            int rate=1ll*a[j][i]*inv%mod;
            for(int k=1;k<=n;k++)a[j][k]=(a[j][k]-1ll*rate*a[i][k]%mod+mod)%mod;
        }
    }
    for(int i=1;i<=n;i++)ans=1ll*ans*a[i][i]%mod;
    ans=(ans*w+mod)%mod;
    return ans;
}
int main(){
    int tim;scanf("%d",&tim);
    while(tim--){
        scanf("%d",&k);t=0;
        for(int i=1;i<=k;i++)scanf("%d",&sum[i]),sum[i]+=sum[i-1];
        for(int i=1;i<k;i++)scanf("%d",&m[i]);
        for(int i=1;i<k;i++){
            while(m[i]--){
                int u,v;scanf("%d%d",&u,&v);
                add(sum[i-1]+u,sum[i]+v);
            }
        }
        for(int i=1;i<=sum[1];i++){
            bfs(i);
            for(int j=1;j<=sum[1];j++)a[i][j]=cnt[sum[k-1]+j];
        }
        printf("%d\n",gauss(sum[1]));
        for(int i=1;i<=sum[k];i++)head[i]=0;
    }
    return 0;
}

[NOI2021] 庆典

首先缩点。然后根据这图的性质一定能找一个拓扑序是一棵树,在最后一条入边连上去即可。于是变成了树上问题。\(k=0,1\) 就显然了。

然后是 \(k\) 更大的情况。可以建虚树,然后变成点有点权边有边权,从 \(s\) 到 \(t\) 的路径并。点数是 \(O(k)\) 的,爆扫一遍就行了。

[NOI2021] 量子通信

因为 \(k\le 15\) 所以拆 \(16\) 段一定有一个相同。然后因为数据随机在某一位为某个数的期望个数是 \(\dfrac n{2^{16}}\approx 6\)。于是暴力扫就行了。

挺卡常的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
typedef unsigned long long ull;
namespace IO{
	char buf[1<<23],*p1=buf,*p2=buf;
	#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
	inline int read(){
		int x=0;char s=gc;
		while(!isdigit(s))s=gc;
		while(isdigit(s))x=(x<<1)+(x<<3)+(s-'0'),s=gc;
		return x;
	}
}
using namespace IO;
int n,m,K;
ull a,b;
ull myRand(ull &k1,ull &k2){
    ull k3=k1,k4=k2;
    k1=k4;
    k3^=(k3<<23);
    k2=k3^k4^(k3>>17)^(k4>>26);
    return k2+k4;
}
unsigned short s[400010][16];
ull S[400010][4];
vector<int>v[16][1<<16];
void gen(int n,ull a1,ull a2){
    for(int i=1;i<=n;i++){
        for(int j=0;j<16;j++){
            for(int k=15;k>=0;k--)s[i][j]|=((myRand(a1,a2)&(1ull<<32))?1:0)<<k;
            v[j][s[i][j]].push_back(i);
        }
        for(int j=0;j<4;j++){
            S[i][j]|=(ull)s[i][j*4]<<48;
            S[i][j]|=(ull)s[i][j*4+1]<<32;
            S[i][j]|=(ull)s[i][j*4+2]<<16;
            S[i][j]|=(ull)s[i][j*4+3];
        }
    }
}
bool lastans;
char ch[110];
unsigned short od[256],tmp[16];
ull TMP[4];
bool check(int id){
    int ans=0;
    for(int i=0;i<4;i++){
        ans+=__builtin_popcountll(TMP[i]^S[id][i]);
        if(ans>K)return false;
    }
    return true;
}
void solve(){
    for(int i=0;i<64;i++){
        unsigned short num;
        char ch=gc;
		if(isdigit(ch))num=ch-'0';
		else if(ch>='A'&&ch<='F')num=10+ch-'A';
		else {i--; continue;}
        for(int j=3;j>=0;j--){
            od[(i+1)*4-j-1]=((num>>j)&1)^lastans;
        }
    }
    K=read();
    for(int i=0;i<16;i++){
        tmp[i]=0;
        for(int j=15;j>=0;j--)tmp[i]|=od[(i+1)*16-j-1]<<j;
    }
    for(int i=0;i<4;i++){
        TMP[i]=0;
        TMP[i]|=(ull)tmp[i*4]<<48;
        TMP[i]|=(ull)tmp[i*4+1]<<32;
        TMP[i]|=(ull)tmp[i*4+2]<<16;
        TMP[i]|=(ull)tmp[i*4+3];
    }
    for(int i=0;i<16;i++){
        for(int x:v[i][tmp[i]]){
            if(check(x)){
                lastans=1;cout<<1<<'\n';
                return;
            }
        }
    }
    lastans=0;cout<<0<<'\n';
}
int main(){
    cin>>n>>m>>a>>b;
    gen(n,a,b);
    while(m--)solve();
    return 0;
}

[NOI2021] 密码箱

首先这题的 key observation 是 \(f\) 是连分数,然后连分数的每个渐进分数的分子分母都是互质的而且可以递推。具体递推式:设第 \(k\) 项为 \(p_k,q_k\),则有

\[p_k=a_kp_{k-1}+p_{k-2}\qquad q_k=a_kq_{k-1}+q_{k-2} \]

\[p_{-1}=1\quad p_0=a_0\qquad q_{-1}=0\quad q_0=1 \]

看到递推想到矩阵维护,每项的矩阵就是 \(\left(\begin{matrix}a_k & 1\\1 & 0\end{matrix}\right)\)。看两种修改:

  1. W:显然 \(\left(\begin{matrix}1 & 0\\1 & 1\end{matrix}\right)\)。
  2. E:不是很显然。第一个操作是左乘个 W 的矩阵,第二个是

    \[\left(\begin{matrix}1 & 0\\1 & 1\end{matrix}\right)^{-1}\left(\begin{matrix}1 & 1\\1 & 0\end{matrix}\right)\left(\begin{matrix}1 & 1\\1 & 0\end{matrix}\right)=\left(\begin{matrix}2 & 1\\-1 & 0\end{matrix}\right) \]

    然后可以发现在 \(a_k=1\) 时两种操作是等价的,于是可以直接看做 \(\left(\begin{matrix}2 & 1\\-1 & 0\end{matrix}\right)\)。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int mod=998244353;
int n,q;
char s[200010];
struct Mat{
    int a[2][2];
    Mat(){memset(a,0,sizeof(a));}
    Mat operator*(const Mat&s)const{
        Mat ret;
        ret.a[0][0]=(1ll*a[0][0]*s.a[0][0]+1ll*a[0][1]*s.a[1][0])%mod;
        ret.a[0][1]=(1ll*a[0][0]*s.a[0][1]+1ll*a[0][1]*s.a[1][1])%mod;
        ret.a[1][0]=(1ll*a[1][0]*s.a[0][0]+1ll*a[1][1]*s.a[1][0])%mod;
        ret.a[1][1]=(1ll*a[1][0]*s.a[0][1]+1ll*a[1][1]*s.a[1][1])%mod;
        return ret;
    }
}P,W,E;
struct node{
    #define lson tree[x].son[0]
    #define rson tree[x].son[1]
    Mat val[2],sum[2][2];
    int size,data,son[2];
    bool fl,rev;
}tree[200010];
int t,rt,x,y,z;
int New(int x){
    tree[++t].size=1;tree[t].data=rand();
    if(x==0){
        tree[t].val[0]=tree[t].sum[0][0]=tree[t].sum[0][1]=W;
        tree[t].val[1]=tree[t].sum[1][0]=tree[t].sum[1][1]=E;
    }
    else{
        tree[t].val[0]=tree[t].sum[0][0]=tree[t].sum[0][1]=E;
        tree[t].val[1]=tree[t].sum[1][0]=tree[t].sum[1][1]=W;
    }
    return t;
}
void pushup(int x){
    tree[x].size=tree[lson].size+tree[rson].size+1;
    tree[x].sum[0][0]=tree[x].val[0];
    if(lson)tree[x].sum[0][0]=tree[lson].sum[0][0]*tree[x].sum[0][0];
    if(rson)tree[x].sum[0][0]=tree[x].sum[0][0]*tree[rson].sum[0][0];
    tree[x].sum[1][0]=tree[x].val[1];
    if(lson)tree[x].sum[1][0]=tree[lson].sum[1][0]*tree[x].sum[1][0];
    if(rson)tree[x].sum[1][0]=tree[x].sum[1][0]*tree[rson].sum[1][0];
    tree[x].sum[0][1]=tree[x].val[0];
    if(rson)tree[x].sum[0][1]=tree[rson].sum[0][1]*tree[x].sum[0][1];
    if(lson)tree[x].sum[0][1]=tree[x].sum[0][1]*tree[lson].sum[0][1];
    tree[x].sum[1][1]=tree[x].val[1];
    if(rson)tree[x].sum[1][1]=tree[rson].sum[1][1]*tree[x].sum[1][1];
    if(lson)tree[x].sum[1][1]=tree[x].sum[1][1]*tree[lson].sum[1][1];
}
void pushfl(int x){
    swap(tree[x].sum[0][0],tree[x].sum[1][0]);
    swap(tree[x].sum[0][1],tree[x].sum[1][1]);
    swap(tree[x].val[0],tree[x].val[1]);
    tree[x].fl^=1;
}
void pushrev(int x){
    swap(tree[x].sum[0][0],tree[x].sum[0][1]);
    swap(tree[x].sum[1][0],tree[x].sum[1][1]);
    swap(lson,rson);
    tree[x].rev^=1;
}
void pushdown(int x){
    if(tree[x].fl){
        if(lson)pushfl(lson);
        if(rson)pushfl(rson);
        tree[x].fl=false;
    }
    if(tree[x].rev){
        if(lson)pushrev(lson);
        if(rson)pushrev(rson);
        tree[x].rev=false;
    }
}
void split(int now,int k,int &x,int &y){
    if(!now){
        x=y=0;return;
    }
    pushdown(now);
    if(tree[tree[now].son[0]].size+1<=k){
        x=now;split(tree[now].son[1],k-tree[tree[now].son[0]].size-1,tree[x].son[1],y);
        pushup(x);
    }
    else{
        y=now;split(tree[now].son[0],k,x,tree[y].son[0]);
        pushup(y);
    }
}
int merge(int x,int y){
    if(!x||!y)return x|y;
    pushdown(x);pushdown(y);
    if(tree[x].data<tree[y].data){
        tree[x].son[1]=merge(tree[x].son[1],y);
        pushup(x);return x;
    }
    else{
        tree[y].son[0]=merge(x,tree[y].son[0]);
        pushup(y);return y;
    }
}
void ins(int x){
    rt=merge(rt,New(x));
}
void fl(int l,int r){
    split(rt,l-1,x,y);
    split(y,r-l+1,y,z);
    pushfl(y);
    rt=merge(merge(x,y),z);
}
void rev(int l,int r){
    split(rt,l-1,x,y);
    split(y,r-l+1,y,z);
    pushrev(y);
    rt=merge(merge(x,y),z);
}
void query(){
    Mat ans=P*tree[rt].sum[0][0];
    printf("%d %d\n",ans.a[0][0],ans.a[1][0]);
}
int main(){
    scanf("%d%d%s",&n,&q,s+1);
    W.a[0][0]=W.a[1][0]=W.a[1][1]=1;
    E.a[0][0]=2;E.a[0][1]=1;E.a[1][0]=mod-1;
    P.a[0][0]=P.a[1][0]=P.a[1][1]=1;
    for(int i=1;i<=n;i++)ins(s[i]=='E');
    query();
    while(q--){
        char od[10];scanf("%s",od);
        if(od[0]=='A'){
            scanf("%s",od);
            ins(od[0]=='E');
        }
        else if(od[0]=='F'){
            int l,r;scanf("%d%d",&l,&r);
            fl(l,r);
        }
        else{
            int l,r;scanf("%d%d",&l,&r);
            rev(l,r);
        }
        query();
    }
    return 0;
}

[NOI2021] 机器人游戏

考虑容斥。对于钦定 \(k\) 个起点合法的情况,容斥系数为 \((-1)^{k+1}\)。

暴力枚举起点集合,然后对于每个位置从起点开始扫一遍,可以得到一些一个格子在经过之后变成 \(0/1/x/1-x\) 的限制。每个格子独立,那么分别看贡献:

  1. 同时有 \(0/1\) 或同时有 \(x/1-x\):空,一种。
  2. 前两个有一个,后两个有一个:两种。
  3. 只有一个:三种。
  4. 爆炸:只能全空。

暴力枚举复杂度 \(O(n^2m2^n)\)。听说改成 dfs 可以去掉一个 \(n\)。

数据范围 \(n\le 32\),考虑折半。枚举最后一个起点 \(p\),发现如果 \(p>\dfrac n2\) 那么走了超过 \(\dfrac n2\) 的全部爆炸。那么对于 \(p\le\dfrac n2\) 的,暴力枚举起点集合。对于剩下的考虑这样一个 dp:设 \(dp_{i,s,0/1}\) 为最后一个位置为 \(i\),前边 \(n-p\) 个是否选取状态为 \(s\),更前边有无被选的起点的方案数,转移考虑是否选当前格子:\(p\) 之前的可能选,\(p\) 必须选,后边的都不能选,然后计算当前串一个格子的贡献即可。

发现所有 \(m\) 个序列的 dp 都是一样的,所以可以用 bitset 压起来整体进行 dp,一次计算是 \(O(\frac m\omega)\) 的,于是总复杂度 \(O(\frac{nm2^{n/2}}\omega)\)。

//It's all about my own private universe, the end where it all begins and the trash recyle shop.
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <bitset>
using namespace std;
const int mod=1000000007;
int n,m,k,ans,pw2[1010],pw3[1010],cnt[1<<16],dp[2][1<<16][2];
char s[110];
bitset<1010>now,vis[1010];
struct node{
    bitset<1010>v[4];
    node operator|(const node&s)const{
        node ans;
        for(int i=0;i<4;i++)ans.v[i]=v[i]|s.v[i];
        return ans;
    }
    int calc(){
        bitset<1010>a,b,c;
        a=now&~((v[0]&v[1])|(v[2]&v[3]));
        b=a&(v[0]^v[1]);c=a&(v[2]^v[3]);
        int cnt1=b.count()+c.count(),cnt2=(b&c).count();
        return 1ll*pw2[cnt2]*pw3[cnt1-2*cnt2]%mod;
    }
}st[1010],tmp[1<<16];
int val[1<<16][2];
void solve(int id){
    now=vis[id-1];
    memset(dp,0,sizeof(dp));dp[0][0][0]=mod-1;
    int cur=0;
    for(int i=0;i<(1<<n-id+1);i++){
        node ret=tmp[i];
        val[i][0]=ret.calc();
        ret.v[2].set();
        val[i][1]=ret.calc();
    }
    const int U=(1<<n-id+1)-1;
    for(int i=1;i<=n;i++){
        cur^=1;
        for(int s=0;s<(1<<n-id+1);s++)dp[cur][s][0]=dp[cur][s][1]=0;
        for(int s=0;s<(1<<n-id+1);s++){
            int t=U&(s<<1),od=s>>n-id;
            if(i!=id){
                if(i<id)dp[cur][t][od]=(dp[cur][t][od]+1ll*dp[cur^1][s][0]*val[t][1])%mod;
                else dp[cur][t][od]=(dp[cur][t][od]+1ll*dp[cur^1][s][0]*val[t][od])%mod;
                dp[cur][t][1]=(dp[cur][t][1]+1ll*dp[cur^1][s][1]*val[t][1])%mod;
            }
            if(i<=id){
                t|=1;
                if(i<id)dp[cur][t][od]=(dp[cur][t][od]-1ll*dp[cur^1][s][0]*val[t][1]%mod+mod)%mod;
                else dp[cur][t][od]=(dp[cur][t][od]-1ll*dp[cur^1][s][0]*val[t][od]%mod+mod)%mod;
                dp[cur][t][1]=(dp[cur][t][1]-1ll*dp[cur^1][s][1]*val[t][1]%mod+mod)%mod;
            }
        }
    }
    for(int i=0;i<(1<<n-id+1);i++)ans=(1ll*ans+dp[cur][i][0]+dp[cur][i][1])%mod;
}
int main(){
    scanf("%d%d",&n,&m);k=n>>1;pw2[0]=pw3[0]=1;
    for(int i=1;i<=m;i++)pw2[i]=2ll*pw2[i-1]%mod,pw3[i]=3ll*pw3[i-1]%mod;
    for(int i=1;i<=m;i++)st[0].v[2][i]=1;
    for(int i=1;i<=m;i++){
        scanf("%s",s+1);
        int len=strlen(s+1);
        int p=2,pos=1;
        for(int j=1;j<=len;j++){
            if(s[j]=='R'){
                st[pos].v[p][i]=1;
                p=2;pos++;
            }
            else if(s[j]=='0')p=0;
            else if(s[j]=='1')p=1;
            else if(s[j]=='*')p^=1;
        }
        st[pos].v[p][i]=1;
        for(int j=pos+1;j<=n+1;j++)st[j].v[2][i]=1;
        for(int j=0;j<n;j++)if(pos+j<=n)vis[j][i]=1;
    }
    for(int i=1;i<(1<<k);i++)cnt[i]=__builtin_popcount(i)&1?mod-1:1;
    for(int i=1;i<=n;i++){
        for(int s=1;s<(1<<k);s++){
            if(__lg(s&-s)>=i)tmp[s]=tmp[s&(s-1)]|st[0];
            else tmp[s]=tmp[s&(s-1)]|st[i-__lg(s&-s)];
            now=vis[__lg(s)];
            cnt[s]=1ll*cnt[s]*tmp[s].calc()%mod;
        }
    }
    for(int i=1;i<(1<<k);i++)ans=(ans-cnt[i]+mod)%mod;
    for(int s=1;s<(1<<k+(n&1));s++)tmp[s]=tmp[s&(s-1)]|st[__lg(s&-s)+1];
    for(int i=k+1;i<=n;i++)solve(i);
    printf("%d\n",ans);
    return 0;
}

标签:int,题解,sum,tree,NOI2021,lson,include,rson
From: https://www.cnblogs.com/gtm1514/p/17549508.html

相关文章

  • 题解 最大加权矩阵
    题目链接虽然是一道橙题,但还是蕴含了重要算法思想——降维思想。如果是一维形式,即最大子段和,我们采取先求前缀和,并固定右端点,减去左边最小的办法求。对于这题,若固定了上下边界,则可以利用列的前缀和将其“压缩”为一维形式,再采取“最大子段和”的方式求解。如下面一个二维矩阵:......
  • 题解 醋溜便当
    题目链接题目让我们找出每个点是否存在长度\(\in[x,k\timesx]\)的回路,若找到一长度为\(a(0<a\lex)\)的回路,那么必然存在\(pa\in[x,k\timesx](p\in\Z)\),若找到长度\(\in[x,k\timesx]\)的回路,直接符合条件。所以问题转化为求是否存在\(\in[1,k\timesx]\)的回路,只需......
  • 【题解】CF gym 104337 G. Guess the Polynomial
    statement:https://codeforces.com/gym/104337/problem/G。即求\(f(x)=\sum\limits_{i=0}^{p-2}a_ix^i\),其中只有不超过\(n\)个\(a_i\)非\(0\)。记:\[\begin{aligned}A_{n}^{k}&=\sum_{i\equivk\pmod{n}}a_i=\frac{1}{n}\sum_{i=0}^{n-1}f(\omega_{n}^{......
  • CF1450C2 题解
    题目传送门再不写题解社贡要掉到\(0\)了。题目分析显然如果\(3\)个格子构成了满足获胜条件的情况,这\(3\)个格子模\(3\)的余数各不相同。那么我们将格子按模\(3\)的余数分为\(3\)类,只要保证相邻\(3\)个格子中有\(2\)个不同就行了,不需要动第\(3\)个。我们令......
  • Facetook Priority Wall 题解
    题目传送门一道模拟题。用一个map存储每个人的优先级因子,然后存进vector里进行排序。难点在于分辨\(X\)和\(Y\)与当前是什么操作。不过需要注意,只要出现了名字就需要输出,且我们认为与你没关系的人不加分。Code#include<bits/stdc++.h>#definelllonglong#define......
  • centos7ping不通主机却能够上网时的问题解决方案
       ......
  • Codeforeces #1844 A~D题解
    Codeforeces#1844A~D题解ASubtractionGame博弈论A+Bproblem由于只有两种数字可选,若石子数量为a+b,先手选完之后必然为a或b,因此后手可以直接选完BPermutations&Primes构造构造方法:35791108642,这样把1放中间可以让最多的区间拿到1,接下来避免同时拿......
  • 「Network」题解
    「CEOI2012」NetworkSolutiontoQuestionⅠ首先缩点(当然也可以不缩?),然后跑一遍DFS即可。//w为联通分量里的节点个数inlinevoiddfs(constint&u){ ans1[u]=w[u]; for(intv:G_scc[u]) dfs(v),ans1[u]+=ans1[v];}SolutiontoQuestionⅡ观察缩完点后......
  • 正方形鱼池题解
    首先这道题\(T\)的范围很小,而\(N\)的范围却很大,所以我们只能枚举树那么我们如何枚举呢,树有上下左右之分,看起来十分难枚举,现在让我们仔细分析一下:水池的边长就等于\(min(上下界的距离,左右界的距离)\)这时我们就可以开始枚举了,我枚举的是左右界那么我们此时就可以发现上下界的两......
  • 题解 [NOIP2011 提高组] 聪明的质监员
    题目链接不难发现,\(W\)越大,\(y_i\)以及\(y\)就越小,\(W\)越小,\(y_i,y\)就越大。所以这是一个二分答案。考虑如何\(check\)。观察\[y_i=\sum\limits_{j=l_i}^{r_i}[w_j\geW]\times\sum\limits_{j=l_i}^{r_i}[w_j\geW]v_j\]不难发现,乘号的前后都是区间和的形式,有......