标签:7.29 int back long leq vector define
CF954F
题意:
有一个\(3*m\)的格子,起点是\((2,1)\),终点是\((2,m)\),每一步可以向正右,右上,右下走,有多少种方案从起点走到终点?
额外有\(k\)个限制,每个形如在\([l_k,r_k]\)范围内,第\(a_k\)行不可进入。
\(k\leq 10^4,m\leq 10^{18}\)
\(2\leq l_k\leq r_k\leq m,1\leq a_k\leq 3\)
题解:
分段矩阵快速幂
如果没有限制,那么将是简单的矩阵快速幂优化动态规划。
发现限制的数量不多,可以分段矩阵优化,每个限制最多会贡献两个端点,端点之间的转移方程是相同的,可以矩阵优化,所以我们不妨把限制的端点设置成关键点,每次转移由一个关键点转移到另一个关键点,然后改变转移方程。
\(m\)非常大,想知道某个位置是否被禁止进入,可以每行开一个\(vector\),在\(vector\)里做前缀和,然后二分查找对应位置。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=5e5+10,mod=1e9+7,inf=2e9;
int n,m;
int c[N],num;
struct matrix
{
int g[4][4];
inline void clear()
{
memset(g,0,sizeof(g));
}
inline matrix operator * (const matrix &t) const
{
matrix ret;
ret.clear();
for(int i=1;i<=3;++i)
{
for(int j=1;j<=3;++j)
{
for(int k=1;k<=3;++k)
{
ret.g[i][j]=(ret.g[i][j]+g[i][k]*t.g[k][j])%mod;
}
}
}
return ret;
}
}T,ans,tran,tmp;
inline matrix matrixfast(matrix x,int k)
{
matrix ret;
ret.clear();
ret.g[1][1]=ret.g[2][2]=ret.g[3][3]=1;
while(k)
{
if(k&1) ret=ret*x;
x=x*x;
k>>=1;
}
return ret;
}
typedef pair<int,int> pr;
vector<pr> q[4];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)
{
int x,l,r;cin>>x>>l>>r;
c[++num]=l-1,c[++num]=r;
q[x].emplace_back(pr(l,1));
q[x].emplace_back(pr(r+1,-1));
}
for(int k=1;k<=3;++k)
{
sort(q[k].begin(),q[k].end());
int s=q[k].size();
for(int i=1;i<s;++i)
{
q[k][i].second+=q[k][i-1].second;
}
}
ans.clear();
ans.g[1][2]=1;
tran.clear();
tran.g[1][1]=tran.g[2][1]=tran.g[1][2]=tran.g[2][2]=tran.g[3][2]=tran.g[2][3]=tran.g[3][3]=1;
sort(c+1,c+num+1);
num=unique(c+1,c+num+1)-c-1;
int pre=1;
for(int i=1;i<=num;++i)
{
int now=c[i];
int len=now-pre;
if(!len) continue;
tmp=tran;
for(int k=1;k<=3;++k)
{
int pos=upper_bound(q[k].begin(),q[k].end(),pr(now,1e18))-q[k].begin()-1;
//if(now==2&&k==2) cout<<k<<' '<<q[k][0].first<<"!!"<<endl;
if(pos>=0&&q[k][pos].second>0)
{
for(int j=1;j<=3;++j) tmp.g[j][k]=0;
}
}
//cout<<now<<"!!"<<endl;
// for(int j=1;j<=3;++j)
// {
// for(int k=1;k<=3;++k)
// {
// cout<<tmp.g[j][k]<<" \n"[k==3];
// }
// }
cout<<'\n';
tmp=matrixfast(tmp,len);
ans=ans*tmp;
pre=now;
}
// matrix qwq=ans;
// qwq=qwq*tran*tran;
// cout<<qwq.g[1][1]<<' '<<qwq.g[1][2]<<' '<<qwq.g[1][3]<<'\n';
//cout<<m-pre<<"!!"<<endl;
tmp=matrixfast(tran,m-pre);
ans=ans*tmp;
cout<<ans.g[1][2]<<'\n';
}
}
signed main()
{
red::main();
return 0;
}
/*
0 5
*/
CF954H
题意:
给定一棵树,深度为\(n\)
给定一个长度为\(n-1\)的序列,第\(i\)个数表示所有深度为\(i\)的节点都有\(a_i\)个儿子。
问树中长度为\(1\sim 2*n-2\)的路径分别有多少条
对\(1e9+7\)取模
\(n\leq 5000,2\leq a_i\leq 10^9\)
题解:
考虑设计动态规划:
我们认为枚举的点是路径的\(LCA:\)
设\(s(i,j)=\prod_{k=i}^ja_k,g[i]=\prod_{k=1}^ia_i\)
第一部分答案是一个端点作为\(lca\)的情况,对于深度为\(i\)的节点,对长度为\(j\)的答案的贡献是\(s(1,i-1)*s(i,i+j-1)=s(1,i+j-1)\)
第二部分答案是没有端点作为\(lca\)的情况,设\(dp[i][j]=s(i+1,i+j-1)\)
这是从深度为\(i\)的节点开始,往下深度为\(j\)的情况,但是因为深度为\(i\)的节点必须恰好选两个儿子,所以\(a_i\)没有算在里面。
这部分对长度为\(k\)的路径的答案的贡献是\(ans[k]=s(1,i-1)*\dbinom{a_i}{2}*(\sum_{j=1}^{k-1} dp[i][j]*dp[i][k-j])\)
可以发现后面是一个卷积式子,用\(fft\)可以优化,就可以做到总复杂度\(O(n^2logn)\)
但是这个题目卡的比较死,不能带\(log\),只能继续优化。
考虑后面的复杂度瓶颈\(\sum_{j=1}^{k-1} dp[i][j]*dp[i][k-j]\)
把\(dp[i][j]\)拆开
\(\sum_{j=1}^{k-1}\frac{g[i+j-1]}{g[i-1]}*\frac{g[i+k-j-1]}{g[i-1]}=\frac{1}{g[i-1]^2}\sum_{j=1}^{k-1}g[i+j-1]g[i+k-j-1]\)
设\(f[k][i]=\sum_{j=1}^{k-1}g[i+j-1]g[i+k-j-1]\)
考虑卷积的性质,可以找一下规律
\(f[2][i]=g[i]*g[i],f[4][i]=g[i]*g[i+2]*g[i+1]*g[i+1]+g[i+2]*g[i]\)
前面的\(g[i]*g[i]\)和后面的\(g[i+1]*g[i+1]\)仅仅是错了一位,由此可以得出递推式
\(f[k][i]=f[k-2][i+1]+2*g[i]g[i+k-2]\)
边界是\(f[2][i]=g[i]^2\)
\(O(n^2)\)预处理这一部分,就可以解决了。
还可以换一种分类方式,虽然思维惯性可能会比较严重,只想着优化刚才的式子,不容易换定义。
这次认为我们枚举的点是路径的一个端点:
定义\(f[i][j]\)是从深度为\(i\)的节点开始,只向下走\(j\)步的方案数。
\(g[i][j]\)是从深度为\(i\)的节点开始,第一步向上走,走\(j\)步的方案数。
\(f[i][j]=f[i+1][j-1]*a[i]\)
\(g[i][j]=g[i-1][j-1]+(a[i]-1)*f[i][j-2]\)
关于\(g\)数组的转移,第一种是继续向上走,第二种是向上一步后选择深度为\(i-1\)节点的另一个儿子进入。
但是本题卡空间,只能开下一个数字,可以先预处理\(f\)数组。
然后把用\(f\)数组代替\(g\)数组,求\(g\)数组的时候,从大到小更新\(j\),先把\(f[i][j]=f[i-1][j-1]\)
而这时\(f[i][j-2]\)还没更新,可以用来更新\(f[i][j]\)
对于每个\(f[i][j]+g[i][j]\)的答案,都要乘上\(s(1,i-1)\)
这种情况下每条路径会被算两遍(从两个端点处各开始一次),最后把答案除以二。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define eps (1e-15)
const int N=5e5+10,mod=1e9+7,inv2=5e8+4,inf=2e9;
const double pi=acos(-1.0);
int n;
int a[N],s[N];
int ans[N];
signed f[5010][10010];
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
s[0]=1;
for(int i=1;i<n;++i)
{
cin>>a[i];
s[i]=1ll*s[i-1]*a[i]%mod;
}
for(int i=n;i>=1;--i)
{
f[i][0]=1;
for(int j=1;j<=n-i;++j)
{
f[i][j]=1ll*f[i+1][j-1]*a[i]%mod;
ans[j]=(ans[j]+1ll*f[i][j]*s[i-1]%mod)%mod;
}
}
for(int i=1;i<=n;++i)
{
for(int j=2*n-2;j>=1;--j)
{
f[i][j]=f[i-1][j-1];
if(i>1&&j>1&&j<=i+n-2) f[i][j]=(f[i][j]+1ll*f[i][j-2]*(a[i-1]-1)%mod)%mod;
ans[j]=(ans[j]+1ll*f[i][j]*s[i-1]%mod)%mod;
}
}
for(int i=1;i<=2*n-2;++i) cout<<1ll*ans[i]*inv2%mod<<' ';
}
}
signed main()
{
red::main();
return 0;
}
/*
aab
*/
CF954I
题意:
给定字符串\(s,t\)
每次操作可以选择一个字符,然后两个字符串中所有该字符变成另一种字符。
定义两个字符串之间的距离为需要最少的操作次数,让两个字符串变得一样。
求对于\(i\in[0,n-m+1]\)为起点,长度为\(|t|\)的\(s\)的子串,和\(t\)的距离是多少?
字符集大小为\(a\sim f\)
\(1\leq|t|\leq|s|\leq 125000\)
题解:
分析题目可以发现,如果两个位置上的字符不同,可以从\(s_u\)向\(t_v\)连一条边,连完边之和,同一连通块内的字符必须变成同一种字符,也就是这个连通块会产生点数\(-1\)的操作数。
有两种解决方案,第一种比较暴力:
枚举哪些字符要变成同一种字符,这部分复杂度是\(Bell(6)=203\)
然后可以用\(KMP\)尝试匹配\(s\)和\(t\),匹配成功就更新答案。
复杂度是\(O(Bell(6)*|s|)\)
另一种方案数考虑优化连边的过程,想要快速知道两种字符之间是否有连边,可以考虑卷积优化匹配:
假如想知道\(a\)和\(b\)有没有连边,先把\(s\)中\(a\)的位置变成\(1\),否则变成\(0\),\(t\)同理。
然后把\(t\)翻转,和\(s\)做卷积。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define eps (1e-15)
const int N=5e5+10,mod=998244353,inf=2e9;
int n,m;
char s[N],t[N];
vector<int> a[6],b[6],ff,g;
int ans[N];
int f[N*6],idx;
inline int find(int k)
{
return f[k]==k?k:f[k]=find(f[k]);
}
const int gi=332748118;
int limit=1,len;
int pos[N];
inline int fast(int x,int k)
{
int ret=1;
while(k)
{
if(k&1) ret=ret*x%mod;
x=x*x%mod;
k>>=1;
}
return ret;
}
inline vector<int> ntt(vector<int> a,int inv)
{
for(int i=0;i<limit;++i)
if(i<pos[i]) swap(a[i],a[pos[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int Wn=fast(inv?3:gi,(mod-1)/(mid<<1));
for(int r=mid<<1,j=0;j<limit;j+=r)
{
int w=1;
for(int k=0;k<mid;++k,w=w*Wn%mod)
{
int x=a[j+k],y=w*a[j+k+mid]%mod;
a[j+k]=(x+y)%mod;
a[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(inv) return a;
inv=fast(limit,mod-2);
for(int i=0;i<limit;++i) a[i]=a[i]*inv%mod;
return a;
}
inline vector<int> add(vector<int> a,vector<int> b,int n,int m)
{
limit=max(n,m);
a.resize(limit),b.resize(limit);
for(int i=0;i<limit;++i) a[i]=(a[i]+b[i])%mod;
return a;
}
inline vector<int> mul(vector<int> a,vector<int> b,int n,int m)
{
limit=1,len=0;
while(limit<n+m) limit<<=1,++len;
a.resize(limit,0),b.resize(limit,0);
for(int i=0;i<limit;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(len-1));
a=ntt(a,1),b=ntt(b,1);
for(int i=0;i<limit;++i) a[i]=a[i]*b[i]%mod;
vector<int> c=ntt(a,0);
c.resize(n+m-1);
return c;
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>(s)>>(t);
n=strlen(s),m=strlen(t);
for(int i=0;i<n;++i)
{
for(int k=0;k<6;++k)
{
a[k].emplace_back((s[i]-'a')==k);
}
}
for(int i=0;i<m;++i)
{
for(int k=0;k<6;++k)
{
b[k].emplace_back((t[i]-'a')==k);
}
}
for(int k=0;k<6;++k) reverse(b[k].begin(),b[k].end());
while(limit<n+m) limit<<=1,++len;
for(int k=0;k<6;++k)
{
a[k].resize(limit,0),b[k].resize(limit,0);
}
for(int i=0;i<limit;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(len-1));
for(int k=0;k<6;++k)
{
a[k]=ntt(a[k],1);
b[k]=ntt(b[k],1);
}
ff.resize(limit),g.resize(limit);
for(int i=0;i<n-m+1;++i)
{
for(int k=0;k<6;++k) f[i*6+k+1]=i*6+k+1;
}
for(int t1=0;t1<6;++t1)
{
for(int t2=t1+1;t2<6;++t2)
{
int x=t1,y=t2;
for(int i=0;i<limit;++i) ff[i]=a[x][i]*b[y][i]%mod;
ff=ntt(ff,0);
swap(x,y);
for(int i=0;i<limit;++i) g[i]=a[x][i]*b[y][i]%mod;
g=ntt(g,0);
for(int i=0;i<n-m+1;++i)
{
if(ff[i+m-1]|g[i+m-1])
{
if(find(i*6+x+1)!=find(i*6+y+1))
{
f[find(i*6+x+1)]=find(i*6+y+1);
++ans[i];
}
}
}
}
}
for(int i=0;i<n-m+1;++i) cout<<ans[i]<<' ';
}
}
signed main()
{
red::main();
return 0;
}
/*
abcd
bcda
*/
CF1687C
题意:
给定两个序列\(A,B\)和\(m\)个区间\([l_m,r_m]\)
如果某个给定的区间满足\(\sum_{i=l}^ra_i=\sum_{i=l}^rb_i\),那么可以把\(A\)数组的这段变成\(B\)数组
最终能不能把整个\(A\)数组变成\(B\)数组?
\(1\leq n,m\leq 2*10^5,1\leq a_i,b_i\leq 10^9,1\leq l_i<r_i\leq n\)
题解:
可以设\(c_i=a_i-b_i\),这样把\(B\)转化成全\(0\)序列。
考虑到区间相等,可以把区间条件转化为前缀和\(s_i=\sum_{j=1}^ic_j\)
那么操作的条件就是,如果\(s_r=s_{l-1}\),可以把整段区间赋值成\(s_{l-1}\)
如果\(s_{l-1}!=0\),那操作没有意义。
所以每次选一个\(s_{l-1}=s_r\)的位置,然后把\([l,r]\)全部赋值成\(0\)。
可以考虑把所有\(0\)的位置放进队列,非零位置放进\(set\),每次把一个位置清零之后,把它从\(set\)放进队列里,用来作为区间端点。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=5e5+10;
int n,m;
int a[N];
typedef pair<int,int> pr;
pr p[N];
vector<int> eg[N];
set<int> s;
int f[N];
inline find(int k){return f[k]==k?k:f[k]=find(f[k]);}
inline void main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int skx;cin>>skx;
while(skx--)
{
cin>>n>>m;
++n;
for(int i=1;i<=n;++i)
{
f[i]=i;
eg[i].clear();
}
for(int i=2;i<=n;++i) cin>>a[i];
for(int i=2;i<=n;++i)
{
int x;cin>>x;
a[i]-=x;
}
for(int i=2;i<=n;++i) a[i]+=a[i-1];
for(int i=1;i<=m;++i)
{
auto &[l,r]=p[i];
cin>>l>>r;
++l,++r;
eg[l-1].emplace_back(r);
eg[r].emplace_back(l-1);
}
queue<int> q;
set<int> s;
for(int i=1;i<=n;++i)
{
if(a[i]) s.insert(i);
else q.push(i);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int t:eg[now])
{
if(a[t]) continue;
int l=min(now,t),r=max(now,t);
auto it=s.lower_bound(l);
while(it!=s.end()&&(*it)<=r)
{
int p=*it;
a[p]=0;
q.push(p);
auto it1=it;++it;
s.erase(it1);
}
}
}
if(s.empty()) cout<<"YES\n";
else cout<<"NO\n";
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
10 5
1 2 1 2 1 2 1 2 1 2
2 1 2 1 2 1 2 1 2 1
1 3
3 5
5 7
7 9
9 10
-1 1 -1 1 -1 1 -1 1 -1 1
*/
CF1710C
题意:
给一个二进制数字\(n\),求满足\(1\leq x,y,z\leq n\)的\(x,y,z\),且满足\((x⊕y),(y⊕z),(x⊕z)\)三条边能构成三角形的方案数。
对\(998244353\)取模
\(0<n<2^{200000}\)
题解:
构成三角形显然要满足两边之和大于第三条边,设三条边分别为\(a,b,c\),观察三条边可以得到任意两条边异或后等于第三条边
\(a⊕b=c\)
那么就要\(a⊕b<a+b\),这种情况下,当且仅当\(a\&b!=0\)。
那么可以数位\(dp\),同时处理三个数字
\(dp[now][a][b][c][x][y][z]\)表示当前处理到第\(now\)为,\(a,b,c\)表示是否是最大前缀,\(x,y,z\)表示之前是否满足了取并不等于\(0\)。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define eps (1e-15)
const int N=3e5+10,inf=2e9,mod=998244353,g=3,gi=332748118;
int n,m;
int dp[N][2][2][2][2][2][2];
char s[N];
inline int dfs(int now,int a,int b,int c,int x,int y,int z)
{
if(now==n)
{
return (x&y&z);
}
if(~dp[now][a][b][c][x][y][z]) return dp[now][a][b][c][x][y][z];
int sum=0;
for(int i=0;i<=1;++i)
{
if(a&&s[now]-'0'<i) continue;
for(int j=0;j<=1;++j)
{
if(b&&s[now]-'0'<j) continue;
for(int k=0;k<=1;++k)
{
if(c&&s[now]-'0'<k) continue;
sum=(sum+dfs(now+1,a&(s[now]-'0'==i),b&(s[now]-'0'==j),c&(s[now]-'0'==k),x|((i^j)&(j^k)),y|((i^k)&(j^k)),z|((i^j)&(i^k))))%mod;
}
}
}
return dp[now][a][b][c][x][y][z]=sum;
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>s;
n=strlen(s);
memset(dp,-1,sizeof(dp));
cout<<dfs(0,1,1,1,0,0,0)<<'\n';
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
CF1681F
题意:
给定一颗\(n\)个节点的树,每条边有一个颜色
求\(\sum_{i=1}^n\sum_{j=i+1}^nf(i,j)\)
\(f(i,j)\)表示\(i\)到\(j\)节点路径上恰好出现一次的颜色的数量
\(1\leq n\leq 2.5*10^5,1\leq c\leq n\)
题解:
既然是算全图贡献,不妨计算每一条边的贡献。
那么我们枚举一条边,要求计算出经过这条边,且路径上没有和这条边相同颜色的路径数量,也就是在边的两侧各取一些点然后乘起来。
设\(f[x]\)表示只走与\(x\)父亲边不同颜色的边,能到达的子节点的数量
另外开一个\(vector<int> g[c]\)表示,在当前遍历到的节点及其祖先中,父亲边颜色为\(c\)的节点序列。
那么每条边\(<now,t,col>\)的答案就是\(f[t]*f[g[col].back()]\)
如何预处理\(f\)呢?初始化\(f[x]=str[x]\),当在子节点中遇到一条颜色为\(col\)的边时,\(f[g[col].back()]-=str[t]\)
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
const int N=5e5+10,inf=2e9;
int n,m,ans;
typedef pair<int,int> pr;
vector<pr> eg[N];
vector<int> g[N];
int str[N],f[N*2];
inline void dfs1(int now,int fa)
{
str[now]=1;
for(auto [t,c]:eg[now])
{
if(t==fa) continue;
g[c].emplace_back(t);
dfs1(t,now);
str[now]+=str[t];
g[c].pop_back();
f[g[c].back()]-=str[t];
}
f[now]+=str[now];
}
inline void dfs2(int now,int fa)
{
for(auto [t,c]:eg[now])
{
if(t==fa) continue;
ans+=f[t]*f[g[c].back()];
g[c].emplace_back(t);
dfs2(t,now);
g[c].pop_back();
}
}
inline void main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;++i)
{
int x,y,z;
cin>>x>>y>>z;
eg[x].emplace_back(pr(y,z));
eg[y].emplace_back(pr(x,z));
}
for(int i=1;i<=n;++i)
{
g[i].emplace_back(i+n);
}
for(int i=1;i<=n;++i) f[i+n]=n;
dfs1(1,0);
dfs2(1,0);
cout<<ans<<'\n';
}
}
signed main()
{
red::main();
return 0;
}
/*
212
*/
标签:7.29,int,back,long,leq,vector,define 来源: https://www.cnblogs.com/knife-rose/p/16538801.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。