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;
}
京公网安备 11010502036488号