D - Infinite Path
题意:给定n个点,a数组,b数组.a[i]表示点 i - > 点a[i] 有一条有向边,b[i]表示边的颜色.
定义:p^1=[ a[1],a[2],a[3]...a[n] ],p^2=[ a[a[1]],a[a[2]],....,a[a[n]] ],p^k 如例子一样迭代k次.求p最小长度满足p集合内点成一个环并且颜色相同.( a数组的元素是1~n的全排列 ) (n<=1e5 )
分析:可以从a数组元素得知,图最终是某m个不相交的环(简单证明可得),那么我们只需要枚举所有的环,然后假设第i个环的长度是k,根据迭代关系,我们只要枚举k的因子进行check颜色更新答案即可.
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { int t; cin>>t; while( t-- ) { int n;cin>>n; vector<int>g(n),c(n),v(n); // v(i) i是否加入环中 for( auto &i:g ) cin>>i,i--; // g(i) 下一位置 for( auto &i:c ) cin>>i; // c(i) 颜色 int ans=n+1; for( int i=0;i<n;i++ ) { if( v[i] ) continue; vector<int> cc; // 存环上的点 while( !v[i] ) { cc.push_back(c[i]); v[i]=1; i=g[i]; } // 最终 i一定会回环起点 int l=cc.size(); for( int i=1;i<=l;i++ ) { if( l%i ) continue; for( int s=0;s<i;s++ ) { int y=1; for( int j=s;j<l;j+=i ) { if( cc[s]!=cc[j] ) y=0; } if( y && i<ans ) ans=i; } } } cout<<ans<<endl; } }
E - Count The Blocks
题意:定义一个数字n,该数字十进制下连续长度为1 ~ len(n)的数字贡献,现在求[1,10^n-1],连续长度为1~len(n)的数字贡献和是多少.答案要模998244353.举个例子:00033440.长度为3的有1个000,长度为2 的有两个33和44,长度为1的有1个0.(n<=10^100000)
分析:
- 枚举当前这位开始向后连续i个相同数字的方案数.
- i=n时,ans=10.否则分两种情况,一种是连续i个相同数字的起始位置在最左端或者最终位置在最右端,第二种情况在中间位置----这种起始位置有(n-i-1)个.对应情况算答案即可.
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=998244353; const int maxn=2e5+10; ll p[maxn]; ll ans[maxn]; int main() { p[0]=1; for( int i=1;i<maxn;i++ ) p[i]=(p[i-1]*10)%mod; ll n; cin>>n; for( int i=1;i<=n;i++ ) { if( i==n ) ans[i]=10; else { ans[i]+=p[n-i]*9*2; ans[i]+=(n-i-1)*9*9*p[n-i-1]; ans[i]%=mod; } } for( int i=1;i<=n;i++ ) cout<<ans[i]<<" "; }
F - AND Segments
题意:给定m个(l,r,x)条件,请构造序列长度为n的数组,满足所有条件[li,ri]的区间所有元素的按位&等于xi,并且序列元素大小 小于2^k.( 1<=l,r<=n<=5e5,k<=30,xi<2^k )
**分析:
- 首先了解按位&的性质.对于[l,r]二进制第i位按位&为0,只要[l,r]内元素有一个0那么便满足条件,如果第i位按位&为1,那么对应所有元素必须为1.
- 那么考虑按二进制位分情况讨论.枚举二进制位,对于当前位 所有条件区间进行建差分数组.使用动态规划dp[i]---最后一个0正好位于位置i,并且它左边的所有0段至少包含一个0.我们只要预处理出当前i为0段区间右端的最大左端点值.然后维护一个前缀和更新即可.
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=998244353; const int maxn=5e5+10; int n,m,k; int l[maxn],r[maxn],a[maxn],b[maxn]; int cl[maxn],dif[maxn]; ll f[maxn],psum[maxn]; ll solve() { for( int i=1;i<=n;i++ ) dif[i]=0,cl[i]=-1; for( int i=1;i<=m;i++ ) { if( b[i] ) ++dif[l[i]],--dif[r[i]+1]; // 差分 else cl[r[i]]=max(cl[r[i]],l[i]); // 限制区间 0段区间 } int leftmost=0; f[0]=psum[0]=1; for( int i=1;i<=n;i++ ) // f(i) 最后一个0正好位于位置 i,并且它左边的所有0段至少包含一个 0 { dif[i]+=dif[i-1]; if( dif[i] ) f[i]=0; else { f[i]=psum[i-1]; if( leftmost ) f[i]=( f[i]-psum[leftmost-1]+mod )%mod; } psum[i]=(f[i]+psum[i-1])%mod; leftmost=max(leftmost,cl[i]); } ll res=0; for( int i=leftmost;i<=n;i++ ) res=(res+f[i])%mod; return res; } int main() { scanf("%d%d%d",&n,&k,&m); for( int i=1;i<=m;i++ ) scanf("%d%d%d",&l[i],&r[i],&a[i]); ll ans=1; for( int i=0;i<k;i++ ) // 对每一个二进制位枚举 { for( int j=1;j<=m;j++ ) b[j]=a[j]>>i & 1; // m条件限制 存于 b[] ans=ans*solve()%mod; } printf("%lld\n",ans); return 0; }