• CF-diary


    (做题方式:瞟题解然后码)

    1238E. Keyboard Purchase

    ( exttt{Difficulty:2200})

    题意

    给你一个长度为 (n) 的由前 (m) 个小写字母组成的字符串 (s) ,定义一个 (m) 个字母的排列 (t) 的代价为 (sum_{i=2}^n |pos_{s_i}-pos_{s_i-1}|) ,其中 (pos_c) 表示 (c)(t) 中的位置。求最小的代价。

    (nle 10^5, mle 20)

    题解

    考虑状压DP,但我们无法记录内部选择的顺序,所以我们可以相当于把所有已选的字符都放在最前面,加入一个字符就对没加入的字符减掉产生的多余的贡献。

    复杂度 (O(2^m imes m^2)) .

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5,M=21;
    char s[N];
    int a[M][M],f[1<<M],n,m;
    int main()
    {
    	scanf("%d%d%s",&n,&m,s+1);
    	for(int i=1;i<n;++i)
    		++a[s[i]-'a'][s[i+1]-'a'],++a[s[i+1]-'a'][s[i]-'a'];
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	for(int s=0;s<(1<<m);++s)
    	{
    		int d=__builtin_popcount(s);
    		for(int i=0;i<m;++i) if(~s&(1<<i))
    		{
    			int t=0;
    			for(int j=0;j<m;++j) if(i!=j) (s&(1<<j))?t+=d*a[i][j]:t-=d*a[i][j];
    			f[s|(1<<i)]=min(f[s|(1<<i)],f[s]+t);
    		}
    	}
    	printf("%d",f[(1<<m)-1]);
    }
    
    

    1234F. Yet Another Substring Reverse

    ( exttt{Difficulty:2400})

    题意

    给你一个由前 (20) 个小写字母组成的字符串 (S) ,你可以翻转一次 (S) 的任意一个子串。

    问翻转后最长的各个字符都不相同的子串的长度。

    题解

    下设字符集大小为 (m)

    注意到翻转一次相当于将两个子串拼起来。

    预处理所有字符不相同的字符串( (m^2) 级别)包含的字母集合,并做一遍高维前缀和求出所有集合的最大子集。

    枚举一个出现过的集合,其答案则为集合大小+其补集的子集大小最大值。

    复杂度为高维前缀和的复杂度,(O(2^m imes m)) .

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5,M=22;
    char a[N];
    int f[1<<M],n,ans;
    bool vis[1<<M];
    int main()
    {
    	scanf("%s",a+1);
    	int n=strlen(a+1);
    	for(int i=1;i<=n;++i)
    	{
    		int s=0,t=0;
    		for(int j=i;j<=n;++j)
    		{
    			if(s&(1<<(a[j]-'a'))) break;
    			else s|=(1<<(a[j]-'a')),f[s]=++t,vis[s]=true;
    		}
    	}
    	const int S=(1<<20)-1;
    	for(int i=0;i<20;++i)
    		for(int s=0;s<=S;++s) if(s&(1<<i)) f[s]=max(f[s],f[s^(1<<i)]);
    	for(int s=0;s<=S;++s)
    		if(vis[s]) ans=max(ans,__builtin_popcount(s)+f[S^s]);
    	printf("%d",ans);
    }
    
    

    1228E. Another Filling the Grid

    ( exttt{Difficulty:2300})

    题意

    (n imes n) 的网格中填 (1sim k) 的数,要求每行每列至少有一个 (1) 。求方案数。

    (nle 250, kle 10^9) .

    题解

    (O(n^3)) 的做法我很难会就扔了……

    考虑经典容斥,枚举有 (i)(j) 列打破限制。

    [sum_{i=0}^nsum_{j=0}^n(-1)^{i+j}inom{n}{i}inom{n}{j}(k-1)^{(n-i)(n-i)}k^{n^2-(n-i)(n-i)} ]

    可以做到 (O(n^2log n)) 。可以推式子优化到 (O(nlog n)) ,可参考官方题解或 这个博客

    代码

    代码是 (O(n^2log n)) 的。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=255,Mod=1e9+7;
    int n,k,c[N][N],ans;
    inline int po(int x, long long y)
    {
    	int r=1;
    	while(y)
    	{
    		if(y&1) r=1ll*r*x%Mod;
    		x=1ll*x*x%Mod, y>>=1;
    	}
    	return r;
    }
    int main()
    {
    	scanf("%d%d",&n,&k);
    	for(int i=0;i<=n;++i) c[i][0]=c[i][i]=1;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%Mod;
    	for(int i=0;i<=n;++i)
    		for(int j=0;j<=n;++j)
    		{
    			int ret=1ll*c[n][i]*c[n][j]%Mod*po(k-1,1ll*(n-i)*(n-j))%Mod*po(k,1ll*n*n-1ll*(n-i)*(n-j))%Mod;
    			ans=(i+j&1?ans+Mod-ret:ans+ret)%Mod;
    		}
    	printf("%d",ans);
    }
    
    

    1223E. Paint the Tree

    ( exttt{Difficulty:2200})

    题意

    给你一棵 (n) 个节点的带权树,你需要给每个节点分配 (k) 种颜色。你的颜色种数是无限的,但每种颜色只能分配给至多 (2) 个节点。定义树的一条边是好的当且仅当边的两个顶点的颜色的交不为空。求最大的好的边的权值和。

    (n,kle 5 imes 10^5),多组数据。

    题解

    对于当前点,我们可以考虑选择若干条边,但是被选择的点只剩下至多 (k-1) 条边可选。

    (f_{u,0/1}) 表示以 (u) 为根的子树,至多能选 (k) / (k-1) 条边能获得的最大代价。

    将所有子节点按照 (f_{v,0}+w_v-f_{v,1}) 从大到小排序,依次选择即可。

    (O(nlog n)) .

    代码

    #include<bits/stdc++.h>
    using namespace std;
    inline int gi()
    {
    	char c=getchar(); int x=0;
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    	return x;
    }
    const int N=5e5+5;
    int n,k,head[N],nxt[N<<1],to[N<<1],wei[N<<1],wf[N];
    long long f[N][2];
    void addedge(int u, int v, int w, int now)
    {
    	nxt[now]=head[u], head[u]=now, to[now]=v, wei[now]=w;
    }
    bool cmp(int x, int y)
    {
    	return f[x][0]+wf[x]-f[x][1]>f[y][0]+wf[y]-f[y][1];
    }
    void dfs(int u, int fa)
    {
    	vector<int> ve;
    	f[u][0]=f[u][1]=0;
    	for(int e=head[u];e;e=nxt[e]) if(to[e]!=fa)
    		dfs(to[e],u),wf[to[e]]=wei[e],ve.push_back(to[e]);
    	sort(ve.begin(),ve.end(),cmp);
    	int chos=0;
    	for(auto v:ve)
    	{
    		if(f[v][0]+wf[v]>f[v][1])
    		{
    			if(chos<k-1) f[u][0]+=f[v][0]+wf[v];
    			else f[u][0]+=f[v][1];
    			if(chos<k) f[u][1]+=f[v][0]+wf[v];
    			else f[u][1]+=f[v][1];
    			++chos;
    		}
    		else f[u][0]+=f[v][1],f[u][1]+=f[v][1];
    	}
    }
    int main()
    {
    	int q=gi();
    	while(q--)
    	{
    		memset(head,0,sizeof(int)*(n+1));
    		n=gi(),k=gi();
    		for(int i=1;i<n;++i)
    		{
    			int u=gi(),v=gi(),w=gi();
    			addedge(u,v,w,i*2-1),addedge(v,u,w,i*2);
    		}
    		dfs(1,0);
    		printf("%lld
    ",f[1][1]);
    	}
    }
    
    

    1223F. Stack Exterminable Arrays

    ( exttt{Difficulty:2600})

    题意

    定义一个入栈出栈的操作序列:顺次扫序列,如果当前数与栈顶不等就将其加入栈中,否则弹出栈顶。

    合法的入栈出栈的操作序列定义为操作完毕后栈为空。

    给你一个长度为 (n) 的序列 ({a_n}(1le a_ile n)) ,求有多少个子段是合法的入栈出栈序列。

    (nle 3 imes 10^5) .

    题解

    题目可以转化成有多少个子段是合法的括号序。

    用 hash 记录每个右端点的栈,然后统计前面有多少个与其相等的即可。

    理论复杂度 (O(nlog n)) ,根据实现可能可以线性。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+5,Mod=1e9+7;
    int n,c[N],st[N];
    unsigned long long a[N],b[N],hsh[N];
    int main()
    {
    	int q; scanf("%d",&q);
    	while(q--)
    	{
    		scanf("%d",&n);
    		int tp=0;
    		for(int i=1,x;i<=n;++i)
    		{
    			scanf("%d",&x);
    			if(st[tp]==x) --tp;
    			else st[++tp]=x,hsh[tp]=hsh[tp-1]*19260817+x;
    			a[i]=b[i]=hsh[tp];
    		}
    		b[n+1]=0;
    		sort(b+1,b+2+n);
    		int m=unique(b+1,b+2+n)-b-1;
    		for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+m,a[i])-b;
    		long long ans=0;
    		c[1]=1;
    		for(int i=1;i<=n;++i) ans+=(c[a[i]]++);
    		printf("%lld
    ",ans);
    		for(int i=1;i<=n;++i) c[a[i]]=0;
    	}
    }
    
    

    1220E. Tourism

    ( exttt{Difficulty:2200})

    题意

    有一个 (n) 个点 (m) 条边的无向图,每个点有一个权值 (w_i),从 (s) 点出发,每次可以走到相邻的点,一条边不能连续走两次。求最大的走到的点的权值和。

    (n,mle 2 imes 10^5, w_ile 10^9) .

    题解

    边双可以从任意一个点进任意一个点出。

    将边双缩点,转变成树上的问题。

    注意到一个点如果所有子树节点(包括自己)边双大小都为 (1) 那么他无法向上返回,否则可以。

    答案即为所有能返回点的权值和+不能返回的点为根的一条最长链权值和(最后走到这个不能返回的点)。

    (O(nlog n)) .

    代码

    #include<bits/stdc++.h>
    using namespace std;
    inline int gi()
    {
    	char c=getchar(); int x=0;
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    	return x;
    }
    const int N=2e5+5;
    vector<int> e1[N],e[N];
    typedef long long ll;
    int n,m,u[N],v[N],w[N],dfn[N],bel[N],low[N],size[N],st[N],tp,tid,bid;
    ll sum[N],f[N],ans,mx;
    unordered_map<int,bool> mp[N];
    void tarjan(int u, int fa)
    {
    	low[u]=dfn[u]=++tid;
    	st[++tp]=u;
    	for(auto v:e1[u]) if(v!=fa)
    	{
    		if(!dfn[v]) tarjan(v,u),low[u]=min(low[u],low[v]);
    		else if(!bel[v]) low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u])
    	{
    		++bid; int v;
    		do
    		{
    			v=st[tp--],bel[v]=bid;
    			++size[bid],sum[bid]+=w[v];
    		} while(v!=u);
    	}
    }
    void dfs(int u, int fa)
    {
    	for(auto v:e[u]) if(v!=fa)
    	{
    		dfs(v,u);
    		size[u]=max(size[u],size[v]);
    		f[u]=max(f[u],f[v]);
    	}
    	f[u]+=sum[u];
    	if(size[u]>1) ans+=sum[u];
    	else mx=max(mx,f[u]);
    }
    int main()
    {
    	n=gi(),m=gi();
    	for(int i=1;i<=n;++i) w[i]=gi();
    	for(int i=1;i<=m;++i)
    	{
    		u[i]=gi(),v[i]=gi();
    		e1[u[i]].push_back(v[i]);
    		e1[v[i]].push_back(u[i]);
    	}
    	for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i,0);
    	for(int i=1;i<=m;++i)
    		if(bel[u[i]]!=bel[v[i]]&&!mp[bel[u[i]]][bel[v[i]]]&&!mp[bel[v[i]]][bel[u[i]]])
    		{
    			e[bel[u[i]]].push_back(bel[v[i]]);
    			e[bel[v[i]]].push_back(bel[u[i]]);
    			mp[bel[u[i]]][bel[v[i]]]=true;
    		}
    	int s=gi();
    	dfs(bel[s],0);
    	printf("%lld",ans+mx);
    }
    
    

    1220F. Gardener Alex

    ( exttt{Difficulty:2600})

    题意

    用一个排列来递归构造一棵二叉树:每次选一个最小的,将左边的放左子树,右边的放右子树。

    给你一个长度为 (n) 的排列,求所有循环移位的排列中构造出的二叉树深度最小的,输出深度和左移的位数。

    (nle 2 imes 10^5)

    题解

    考虑 (a_x)(a_y) 的祖先,当且仅当 (a_x=min_{i=min(x,y)}^{max(x,y)}{a_i})

    预处理出每个点的祖先数量,考虑维护将最左边的点放到最右边造成的影响,会造成左边一段区间祖先数量-1,右边一段区间+1。移到右边后自己的祖先数量也容易计算。用线段树维护区间祖先数量,ST表+二分找区间端点即可。

    (O(nlog n)) .

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=400005;
    int n,lg[N],rid[N],a[N],b[N],st[N<<2],tg[N<<2],f[N][25];
    #define lx (x<<1)
    #define rx (x<<1|1)
    void build(int x, int l, int r)
    {
    	if(l==r)
    	{
    		st[x]=b[l];
    		return ;
    	}
    	int mid=l+r>>1;
    	build(lx,l,mid),build(rx,mid+1,r);
    	st[x]=max(st[lx],st[rx]);
    }
    void pushdown(int x)
    {
    	if(!tg[x]) return ;
    	tg[lx]+=tg[x],tg[rx]+=tg[x];
    	st[lx]+=tg[x],st[rx]+=tg[x];
    	tg[x]=0;
    }
    void update(int x, int l, int r, int sl, int sr, int w)
    {
    	if(sl>sr) return ;
    	if(sl<=l&&r<=sr)
    	{
    		st[x]+=w,tg[x]+=w;
    		return ;
    	}
    	pushdown(x);
    	int mid=l+r>>1;
    	if(sl<=mid) update(lx,l,mid,sl,sr,w);
    	if(sr>mid) update(rx,mid+1,r,sl,sr,w);
    	st[x]=max(st[lx],st[rx]);
    }
    int qq(int x, int l, int r, int s)
    {
    	if(!s) return 0;
    	if(l==r) return st[x];
    	pushdown(x);
    	int mid=l+r>>1;
    	return (s<=mid?qq(lx,l,mid,s):qq(rx,mid+1,r,s));
    }
    int query(int x, int l, int r, int sl, int sr)
    {
    	if(sl<=l&&r<=sr) return st[x];
    	pushdown(x);
    	int mid=l+r>>1,ans=0;
    	if(sl<=mid) ans=query(lx,l,mid,sl,sr);
    	if(sr>mid) ans=max(ans,query(rx,mid+1,r,sl,sr));
    	return ans;
    }
    int qmin(int l, int r)
    {
    	int t=lg[r-l+1];
    	return min(f[l][t],f[r-(1<<t)+1][t]);
    }
    int findl(int l, int r, int w)
    {
    	while(l<=r)
    	{
    		int mid=l+r>>1;
    		if(qmin(l,mid)<w) r=mid-1;
    		else l=mid+1;
    	}
    	return r+1;
    }
    int findr(int l, int r, int w)
    {
    	while(l<=r)
    	{
    		int mid=l+r>>1;
    		if(qmin(mid,r)<w) l=mid+1;
    		else r=mid-1;
    	}
    	return l-1;
    }
    void init(int l, int r, int d)
    {
    	if(l>r) return ;
    	int x=rid[qmin(l,r)];
    	b[x]=d;
    	init(l,x-1,d+1),init(x+1,r,d+1);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]),rid[a[i]]=i,a[i+n]=a[i];
    	for(int i=1;i<=(n<<1);++i) f[i][0]=a[i];
    	for(int j=1;(1<<j)<=(n<<1);++j)
    		for(int i=1;i+(1<<j-1)<=(n<<1);++i)
    			f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
    	init(1,n,1);
    	build(1,1,(n<<1));
    	int del=0,ans=query(1,1,(n<<1),1,n);
    	for(int i=1;i<n;++i)
    	{
    		int lp=findl(i+1,i+n-1,a[i]),rp=findr(i+1,i+n-1,a[i]);
    		update(1,1,(n<<1),i+1,lp-1,-1);
    		update(1,1,(n<<1),rp+1,i+n-1,1);
    		if(a[i]==1) update(1,1,(n<<1),i+n,i+n,1);
    		else update(1,1,(n<<1),i+n,i+n,qq(1,1,(n<<1),rp)+1);
    		int tmp=query(1,1,(n<<1),i+1,i+n);
    		if(tmp<ans) ans=tmp,del=i;
    	}
    	printf("%d %d
    ",ans,del);
    }
    

    1216F. Wi-Fi

    ( exttt{Difficulty:2300})

    题意

    (n) 个位置,有一些位置可以放基站,在位置 (i) 建设基站覆盖信号的范围为 ([max(1,i-k),min(n,i+k)]),代价为 (i) ,未被基站覆盖的位置 (j) 需要花费 (j) 的代价直接连到信号塔。问信号覆盖所有点的最小代价。

    (n,kle 2 imes 10^5) .

    题解

    显然一个点的决策有三种:

    • 被能覆盖它的最左边的基站覆盖;
    • 被右边能覆盖它的最近的基站覆盖;
    • 没有基站覆盖,直接连。

    根据以上三个决策分别 DP 即可。 (O(n))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5;
    int n,k;
    char s[N];
    long long f[N];
    int main()
    {
    	scanf("%d%d%s",&n,&k,s+1);
    	for(int i=1;i<=n;++i) s[i]-='0';
    	int j=1;
    	for(int i=1;i<=n;++i)
    	{
    		for(;j<i&&(j<i-k||!s[j]);++j);
    		if(s[j]) f[i]=f[max(0,j-k-1)]+j;
    		else f[i]=f[i-1]+i;
    		if(s[i])
    			for(int j=i-1;j>=i-k&&j>=0&&!s[j];--j) f[j]=min(f[j],f[i]);
    	}
    	printf("%lld",f[n]);
    }
    

    1215E. Marbles

    ( exttt{Difficulty:2200})

    题意

    (n) 个珠子 , 第 (i) 个珠子颜色是 (c_i) 。每次操作把相邻的两个珠子交换。现在要把相同颜色的珠子排列在相连的一段,问至少要多少次操作 。

    (nle 4 imes 10^5, c_ile 20)

    题解

    状压DP睿智题,可以轻松预处理 (c[i][j]) 表示把颜色为 (i) 的全部移到 (j) 的外面的步数。

    (O(nm+2^m imes m)) ,其中 (m) 为颜色集大小。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=4e5+5,M=22;
    int n,a[N];
    long long c[M][M],f[1<<M];
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	for(int x=1;x<=20;++x)
    	{
    		int r=0;
    		for(int i=n;i;--i)
    		{
    			if(a[i]==x) ++r;
    			else c[x-1][a[i]-1]+=r;
    		}
    	}
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	for(int s=0;s<(1<<20);++s)
    	{
    		for(int i=0;i<20;++i) if(~s&(1<<i))
    		{
    			long long r=0;
    			for(int j=0;j<20;++j) if(i!=j&&(~s&(1<<j))) r+=c[i][j];
    			f[s^(1<<i)]=min(f[s^(1<<i)],f[s]+r);
    		}
    	}
    	printf("%lld",f[(1<<20)-1]);
    }
    

    1208F. Bits And Pieces

    ( exttt{Difficulty:2600})

    题意

    给你一个长度为 (n) 的序列 ({a_n}),求 (max{a_i|(a_j&a_k)}(i<j<k)) .

    (nle 10^6, a_ile 2 imes 10^6) .

    题解

    经典套路和模型还是要熟悉。

    模型是:对于一个 (i) ,考虑求一个 (j>i) ,使得 (a_i|a_j) 最大。预处理每个数出现的最右位置,并做一个高维后缀和求出每个集合出现的最右位置。从高往低位贪心,(a_i)(1) 的位不考虑。如果当前数加上当前位的集合出现的最右位置在 (i) 的右边就加上当前位。

    本题套上一个 (a_j&a_k) 依然不难,相当于把最右位置改成次右位置。直接做即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    inline int gi()
    {
    	char c=getchar(); int x=0;
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    	return x;
    }
    const int N=3e6+5;
    int n,m,a[N],f[N],g[N],ans;
    int main()
    {
    	n=gi();
    	for(int i=1;i<=n;++i)
    	{
    		a[i]=gi();
    		g[a[i]]=f[a[i]],f[a[i]]=i;
    		m=max(m,a[i]);
    	}
    	int l; for(l=0;(1<<l)<=m;++l); --l;
    	for(int j=0;j<=l;++j)
    		for(int i=0;i<=m;++i)
    			if(!(i&(1<<j)))
    			{
    				if(f[i^(1<<j)]>f[i]) g[i]=f[i],f[i]=f[i^(1<<j)];
    				else if(f[i^(1<<j)]>g[i]) g[i]=f[i^(1<<j)];
    				if(g[i^(1<<j)]>f[i]) g[i]=f[i],f[i]=g[i^(1<<j)];
    				else if(g[i^(1<<j)]>g[i]) g[i]=g[i^(1<<j)];
    			}
    	for(int i=1;i<=n;++i)
    	{
    		int now=0;
    		for(int j=l;~j;--j)
    			if(!(a[i]&(1<<j))&&g[now|(1<<j)]>i) now|=(1<<j);
    		ans=max(ans,now|a[i]);
    	}
    	printf("%d",ans);
    }
    
    

    1187F. Expected Square Beauty

    ( exttt{Difficulty:2600})

    题意

    对于一个正整数序列 ({x_n}),定义 (B(x)) 表示相同的数的极长连续段的数量。

    (n) 个区间 ([l_i,r_i])(x_i)([l_i,r_i]) 中随机取值。求 (B(x)^2) 的期望。

    (nle 2 imes 10^5,l_i<r_ile 10^9) .

    题解

    (f(i)=[x_i eq x_{i-1}]) ,特别的,(f(1)=1) (即 (P(x_1 eq x_0)=1) )。

    [E(B(x)^2) = E((sum_{i=1}^n f(i))^2)=sum_{i=1}^nsum_{j=1}^n E(f(i)cdot f(j)) ]

    (|i-j|>1)(E(f(i)cdot f(j))=E(f(i))cdot E(f(j))=P(x_i eq x_{i-1})cdot P(x_j eq x_{j-1})) .

    (i+1=j) ,原式 (=P(x_i eq x_{i-1}and x_i eq x_{i+1}))

    [=1-P(x_i=x_{i-1})-P(x_i= x_{i+1})+P(x_i= x_{i-1} and x_i= x_{i+1}) ]

    (i=j) ,原式 (=P(x_i=x_{i-1})) .

    直接计算即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+5,Mod=1e9+7;
    int l[N],r[N],p[N],suf[N],n,ans;
    #define mul(x,y) (1ll*(x)*(y)%Mod)
    int add(int x, int y)
    {
    	return (x+y>=Mod?x+y-Mod:x+y);
    }
    inline int inv(int x)
    {
    	int y=Mod-2,r=1;
    	while(y)
    	{
    		if(y&1) r=mul(r,x);
    		x=mul(x,x), y>>=1;
    	}
    	return r;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i) scanf("%d",&l[i]);
    	for(int i=1;i<=n;++i) scanf("%d",&r[i]);
    	p[1]=1;
    	for(int i=2;i<=n;++i)
    		p[i]=mul(max(0,min(r[i],r[i-1])-max(l[i],l[i-1])+1),inv(mul(r[i]-l[i]+1,r[i-1]-l[i-1]+1))),
    		p[i]=(1-p[i]+Mod)%Mod;
    	suf[n]=p[n];
    	for(int i=n-1;i;--i) suf[i]=add(suf[i+1],p[i]);
    	for(int i=1;i<=n-2;++i) ans=add(ans,mul(p[i],suf[i+2]));
    	for(int i=1;i<n;++i)
    	{
    		int tmp1=max(0,min(r[i],min(r[i-1],r[i+1]))-max(l[i],max(l[i-1],l[i+1]))+1),
    			tmp2=mul(r[i]-l[i]+1,mul(r[i-1]-l[i-1]+1,r[i+1]-l[i+1]+1)),
    			tmp3=((1-(1-p[i])-(1-p[i+1]))%Mod+Mod)%Mod,
    			tmp=add(tmp3,mul(tmp1,inv(tmp2)));
    		ans=add(ans,(i==1?p[2]:tmp));
    	}
    	ans=mul(2,ans);
    	for(int i=1;i<=n;++i) ans=add(ans,p[i]);
    	printf("%d",ans);
    }
    
    

    1168C. And Reachability

    ( exttt{Difficulty:2400})

    题意

    给你一个长度为 (n) 的序列 ({a_n}) 。定义 (x) 可到达 (y) 当且仅当存在一个 (a_xsim a_y) 的子序列满足相邻两项按位与大于 0 。

    (q) 次询问,每次询问 (x) 能否到达 (y)

    (n,q,a_ile 3 imes 10^5) .

    题解

    感觉又裸搬题解了,真的菜。

    考虑预处理 (f(i,j)) 表示 (a_i) 能跳到的最近的第 (j) 位为 (1) 的位置,类似序列自动机。

    转移和询问都比较显然。

    预处理和处理询问都比较显然。

    预处理的复杂度貌似是两个 (log) ,询问是一个。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    inline int gi()
    {
    	char c=getchar(); int x=0;
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    	return x;
    }
    const int N=3e5+5,L=20;
    int n,q,a[N],f[N][L+5],l[L+5];
    int main()
    {
    	n=gi(),q=gi();
    	for(int i=1;i<=n;++i) a[i]=gi();
    	for(int j=0;j<L;++j) l[j]=n+1,f[n+1][j]=n+1;
    	for(int i=n;i;--i)
    	{
    		for(int j=0;j<L;++j) f[i][j]=n+1;
    		for(int j=0;j<L;++j) if(a[i]&(1<<j))
    		{
    			for(int k=0;k<L;++k) f[i][k]=min(f[i][k],f[l[j]][k]);
    			f[i][j]=l[j]=i;
    		}
    	}
    	while(q--)
    	{
    		int x=gi(),y=gi();
    		bool flag=false;
    		for(int i=0;i<L;++i) if(a[y]&(1<<i)&&f[x][i]<=y) flag=true;
    		puts(flag?"Shi":"Fou");
    	}
    }
    
    
  • 相关阅读:
    基于live555的视频直播 DM368IPNC RTSP分析
    C语言中的二级指针(双指针)
    SQL中的IF ELSE(CASE语句的使用)
    Ubuntu下Postgres安装与配置
    虚拟机+ubuntu 图形界面和终端界面的切换
    中科院 2014年工程硕士入学专业课笔试考场安排
    中科院 2014年GCT考前辅导课程安排
    中科院 工程硕士专业课 复试考试前的辅导安排
    中国科学院大学工程管理与信息技术学院 2014年招收以下八个领域在职工程硕
    中国科学院大 工程管理与信息技术学院 在职工程硕士资格审查和复试重要通知
  • 原文地址:https://www.cnblogs.com/farway17/p/11705878.html
Copyright © 2020-2023  润新知