C:Accept
用一个变量sum记录总和,会发现对于序列中a[i]的值,再经过s1,s2……sn加和时,
它所具有的权值为a[i]*i;
所以在进行t次删除尾节点时,对sum进行减去a[i]*i也就是减去a[i]权值的操作。
在进行添加尾节点的时候,进行sum+=新增节点权值的操作,便可以维护最终答案sum
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M (int)1e4+10
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const ll p=998244353;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll n,q;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
ll a[N];
void solve()
{
ll q;cin>>q;
ll cnt=0;
Int sum=0;
while(q--)
{
ll t,v;cin>>t>>v;
while(t>=1&&cnt>=1)
{
sum-=a[cnt]*cnt;
cnt--;
t--;
}
a[++cnt]=v;
sum+=v*cnt;
ll x=sum%1000000007;
cout<<x<<'\n';
printf("%lld",x);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
H:Accept
题意是有n个队伍要参加两场比赛,但是一个队伍只能参加一场比赛,也就是说,
如果这个队伍出现在两个比赛的可参加名单里,就可以随意安排这只队伍参加两场中的
一场比赛。
题目给出的队伍参加队伍的状态,也就是解题数和罚时,权值解题数>罚时
让我们输出主角队能获得的最好名次。
很明显,我们需要记录主角队的两次比赛不同的状态,计算它在对应比赛所能取得的最好名次,在一场比赛中,主角靠自身能力到达x名次,为了让主角取得更好的名次,我们会希望,
比主角排名靠前的队伍不要参加本场比赛,也就是让那些能参加两场比赛的队伍去参加另一场比赛,遍历前1~x名,如果发现该队伍名字出现两次,那么让主角排名靠前,也就是x--。
输出两场比赛主角队所能取得的最好排名为止。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M (int)1e4+10
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const ll p=998244353;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll n,q;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
struct node
{
string name;
ll s,t;
}a[N],b[N];
bool cmp(node x,node y)
{
if(x.s!=y.s)return x.s>y.s;
else return x.t<y.t;
}
void solve()
{
ll n;cin>>n;
unordered_map<string,ll>mp;
ll ff1,ss1;
for(int i=1;i<=n;i++)
{
cin>>a[i].name>>a[i].s>>a[i].t;
mp[a[i].name]++;
if(a[i].name=="lzr010506")
{
ff1=a[i].s;
ss1=a[i].t;
}
}
ll m;cin>>m;
ll ff2,ss2;
for(int i=1;i<=m;i++)
{
cin>>b[i].name>>b[i].s>>b[i].t;
mp[b[i].name]++;
if(b[i].name=="lzr010506")
{
ff2=b[i].s;
ss2=b[i].t;
}
}
sort(a+1,a+1+n,cmp);
sort(b+1,b+1+m,cmp);
ll l=1,r=n;
while(l<r)
{
ll mid=l+r>>1;
if(a[mid].s<ff1||(a[mid].s==ff1&&a[mid].t>=ss1))r=mid;
else l=mid+1;
}
ll now=l;
for(int i=1;i<l;i++)
{
if(mp[a[i].name]>=2)now--;
}
l=1,r=m;
while(l<r)
{
ll mid=l+r>>1;
if(b[mid].s<ff2||(b[mid].s==ff2&&b[mid].t>ss2))r=mid;
else l=mid+1;
}
ll ans=l;
for(int i=1;i<l;i++)
{
if(mp[b[i].name]>=2)ans--;
}
cout<<min(now,ans)<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
A:
赛时:
题意是对于所有的长度为n的序列,且每个元素都是非负整数并且小于2^m,问这些序列中,存在至少一个子序列的按位与和为1的序列有多少个。
对于这一题首先打了个表,摸清了一下大体规律。
我的思路是
先构造1~n按位与和为1的序列,那么剩下的元素便可以任意搭配。
对于长度为k合法序列来说,除了2^0位必须是1,其他位必须不能全是1
也就是说,对于2^1~2^m-1这些位数的情况共有Ck 0+Ck 1+…Ck k-1种情况,也就是
2^k-1种情况,将2^1~2^m-1这些位数的情况数相乘便是长度为k的合法序列情况数
对于长度为n的合法序列,只要其中存在长度为1~n的合法序列之一即可,并与剩下元素的情况数 spid(spid(2,m,q),n-i,q))相乘即可,但是我的解法只有二十多分,我觉得应该是在对
这k个数与剩下元素之间的排序存在问题。
补题:Accept
补题时咨询了学长学姐,发现我的方法存在重复计算的地方
补题思路1:
--对于长度为i的合法序列,需要为它提供i个位置,需要用到组合数,也就是C(n,i)
--对于长度为i的合法序列,可以发现,如果再继续搭配2^0位为1的数字,也就是奇数,会组成长度为i+1的合法序列,所以其他剩余数字的选择不能是奇数,也就是情况数不能包含2^0位。
--所以我的做法就是计算出长度为i的合法序列的情况数,也就是
ll t=1;
ll x=spid(2,i,q);///每一位不能同时为1
///那么数量就是2^i-1
if(x==0)x=q-1;
else x--;///减一操作
x=spid (x,m-1,q);///总共还剩m-1位
t=t*x%q;///当前长度为i的合法序列情况数
然后将剩余的偶数情况数乘上去
t=t*spid(p,n-i,q)%q;再乘上组合数即可。但很明显,不能很好的解决问题。
补题思路2:
用扩展欧几里得(题目给的模数不一定是质数)算出C(n,i)的组合数时发现,到一定大小后,求出来的组合数都是0(为此我换了很多欧几里得板子),后面经指点发现这实际上是逆元不存在的情况,因为c(n,i)=c[n]/(c[i]*c[n-i])。
补题思路3:
求逆元的方式无法解决组合数问题,其实组合数的球法不唯独求逆元,用杨辉三角公式推也是可以的。
C(n,m)=c(n-1,m-1)+c(n-1,m)
有两种形式,一种是dfs求单个组合数,也有直接打表的n^2复杂度放数组里,我选择了第二种:
C[0][0]=1;
for(int i=1; i<=n; i++)
{
for(int j=0; j<=i; j++)
{
if(!j)C[i][j]=1;
else C[i][j]=(C[i-1][j-1]+C[i-1][j])%q;
}
}
‘构造完后c[n][i]便是所要求的组合数,再把原来的答案进行修正,
t=t*spid(p,n-i,q)%q*C[n][i]%q;
ans=(ans+t)%q;
最后输出ans,便可以accept该问题。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M (int)1e4+10
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const ll p=998244353;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
ll n,m,q;
ll C[6000][6000] = { 0 };
void calC()
{
C[0][0]=1;
for(int i=1; i<=n; i++)
{
for(int j=0; j<=i; j++)
{
if(!j)C[i][j]=1;
else C[i][j]=(C[i-1][j-1]+C[i-1][j])%q;
}
}
}
void solve()
{
cin>>n>>m>>q;
calC();
ll ans=0;
ll p=spid(2,m-1,q);///剩余数字的随机状态数
ll a,b;///用于计算逆元的
for(int i=1; i<=n; i++)
{
ll t=1;
ll x=spid(2,i,q);///每一位不能同时为1
///那么数量就是2^i-1
if(x==0)x=q-1;
else x--;///减一操作
x=spid (x,m-1,q);///总共还剩m-1位
t=t*x%q;
t=t*spid(p,n-i,q)%q*C[n][i]%q;
ans=ans+t;
///t*spid(p,n-i,q)是剩余点随机状态
///c[n]/(c[i]*c[n-i])是从n个数里面挑i个位置给合法序列
ans%=q;
}
cout<<ans%q<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
I:预处理,时间复杂度n*m*4 赛后 Accept
因为光路可逆,所以在光路图中只能存在链或者环,题解说只要把他们分别抽
出来就可以。但很明显难点就是在于怎么抽取这些奇怪的东东
补题思路1:
对于链,很明显是贯穿于我们的网格图的,所以我们可以采用从外射入的方式反向找到我们想要找到的链。
补题思路2:根据补题思路1,我们就可以找到所有的链,那么剩下的便是环
解题思路:
我们肯定需要建立一个数组ans[2000][2000][4],最后一个维度代表的是方向
对于链:
根据补题思路1,由于是一条链,那么必然没有其他分支,因为光路可逆。所以可以用dfs跑(也可以认为是bfs,毕竟就一条线),我们可以从一头跑到另一头不重复。
所以我们需要写一个专门针对链的solve1函数,为了记录路径,还需在全局开个vector<tuple<ll,ll,ll>>vec记录路径,tuple用法和pair差不多,相当于结构体。
对于每一个边界点,从头跑到尾写一个dfs函数,退出条件显然是越界,并把到达的点放进vec里。
这时候将vec倒置,因为方向原本是-> -> -> ->,为了方便for(auto )遍历,所以倒置
这时候额外开一个存坐标的set,遍历vec时如果遇到镜子,便放进set里(函数:istan)
然后让当前点ans[a][b][c]=st.size();
对于环:
根据补题思路2,可以知道剩下的点必然成环,那么用记忆化搜索的方式跑一下所有点即可
比如对一个点进行某个方向的dfs,最后必然回到这个点。这时候你路径上的每个点,也一样是vec中的元素,都应该等于这条环路径上的所有镜子,也就是st.size();为了防止重复跑,所以可以开一个vis[2000][2000][4]来记录当前这个点是否已经跑过。也就是说,环路径bfs的退出条件是vis[x][y][dir]==1。
这时候ans数组就已经构造好了,根据题目输入的信息,O(1)时间复杂度查询答案即可。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M (int)1e4+10
#define ull unsigned long long
#define MAX_LOG 21
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const ll p=998244353;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
typedef pair<double,double>PFF;
#define tll tuple<ll,ll,ll>
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///小香猪的代码块·香香猪猪惹人怜~
///秋雨想睡觉
const ll maxn= (ll)1e3+ 10;
char ch[maxn][maxn];
ll n, m, ans[maxn][maxn][4];
ll vis[maxn][maxn][4];
vector<tll> vec;
bool istan(ll x, ll y, ll dir) // 标记某个状态是否有经过镜子的反射
{
char c=ch[x][y];
if(c=='/'||c =='\\')
return 1;
if(c=='-'&&(dir==0||dir==1))
return 1;
if(c=='|'&&(dir==2||dir== 3))
return 1;
return 0;
}
void dfs(int x, int y, int dir)
{
// 根据题意模拟反射过程,越界或者访问过退出
if(x<1||x>n||y<1||y>m) // 链的退出条件
return;
if(vis[x][y][dir]) // 环的退出条件
return;
vec.push_back(tll(x, y, dir));
vis[x][y][dir]=1;
if(ch[x][y]=='/')
{
if(dir==0)
dfs(x,y+1,3);
else if(dir==1)
dfs(x,y-1,2);
else if(dir==2)
dfs(x+1,y,1);
else
dfs(x-1,y,0);
}
else if(ch[x][y]=='\\')
{
if(dir==0)
dfs(x,y-1,2);
else if (dir== 1)
dfs(x,y+1,3);
else if(dir==2)
dfs(x-1,y,0);
else
dfs(x+1,y,1);
}
else if(ch[x][y] == '-')
{
if(dir==0)
{
dfs(x + 1,y,1);
}
else if(dir==1)
{
dfs(x-1,y,0);
}
else if(dir==2)
{
dfs(x,y-1,2);
}
else
dfs(x,y+1,3);
}
else if(ch[x][y]=='|')
{
if(dir==0)
dfs(x-1,y,0);
else if(dir==1)
dfs(x+1,y,1);
else if(dir==2)
dfs(x,y+1,3);
else
dfs(x,y-1,2);
}
}
void solve1(int x, int y, int dir)
{
// 处理链
vec.clear();
dfs(x,y,dir);
reverse(vec.begin(),vec.end()); // 倒叙遍历状态更新答案
set<PLL> s;
for(auto [a,b,c]:vec)
{
if(istan(a, b, c))
s.insert(PLL(a, b));
ans[a][b][c] = s.size();
}
}
void solve2(int x, int y, int dir)
{
// 处理环
vec.clear();
dfs(x, y, dir);
set<PLL> s;
for (auto [a, b, c] : vec)
{
if (istan(a, b, c))
s.insert(PLL(a, b));
}
ll len=s.size();
for (auto [a, b, c] : vec)
ans[a][b][c]=len; /// 环上的答案都一样
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin>>ch[i][j];
/// 从边界射入的光线一定是链
for (int i = 1; i <= n; i++)
solve1(i,1,3), solve1(i,m,2);
for (int j=1;j<=m;j++)
solve1(1, j, 1), solve1(n, j, 0);
///还未访问的状态,一定是环上的状态
///处理环
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=0; k<4;k++)
if (!vis[i][j][k])
{
solve2(i, j, k);
}
int q;
cin >> q;
unordered_map<string,ll> mp;
mp["above"]=0;
mp["below"]=1;
mp["left"]=2;
mp["right"]=3;
while (q--)
{
ll x,y;
string dir;
cin>>x>>y>>dir;
// 先根据方向偏移一格
ll d=mp[dir];
if (d==0)
x--;
else if(d==1)
x++;
else if(d==2)
y--;
else
y++;
cout<<ans[x][y][d]<<"\n";
}
}
///秋雨真的想睡觉
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///记忆是梦的开场白
B:
题意:在A的基础上删去只有一个合法子序列的序列。
解题思路:对于只有一个合法子序列的序列,我们可以发现,合法子序列全由奇数构成,那只有一个合法子序列的序列的特点是,我们不可以将这个合法子序列的任意一个奇数删去,并且还能得到一个合法子序列。那么枚举这种合法子序列的情况即可。
解题步骤1:由于数字较大,所以我们可以预处理出2的几次方的数组(虽然后期实测不弄这个也能过,但确实写起来方便点了)。
根据解题思路,会发现我需要计算这种特殊的奇数子序列,这种特殊的奇数子序列的特点是,每个奇数在2^1~m-1次方位上至少得有一个特殊0.特殊0就是在这长度为i的奇数子序列中,对于2^t(1<=t<=m-1)上存在一个唯一的0,其他数的2^t位都是1。这样就能保证这个奇数如果被删除,会导致2^t&出来会得到1,从而整体得不到数字1。
这里便需要应用到球盒问题,和第二类斯大林数类似。
球盒问题:我们设dp数组,dp[i][j]表示i个数,对于j个特殊位(即特殊0)的方案数,很明显会发现dp[i][j]可以由i*dp[i-1][j-1]得到(新来的数占据了一个新的特殊位),也可以由i*dp[i][j-1]得到(给i个数再分配一个特殊位)。从而得到递推式dp[i][j]=i*(dp[i-1][j-1]+dp[i][j-1])。
解题步骤2:同样枚举合法奇数子序列的数量2~n。
根据A题,我们可以计算出其余偶数的情况数= pow2[(n - i)*(m - 1)]%q*C[n][i]%q;
如果这i个数每个数都应该由1个以上的特殊位,那么我们必须从dp[i][i]开始枚举起, 到dp[i][m-1]。同时还需要找出这j的特殊位是哪几位的方案数,也就是C[m-1][t]。
特殊位的方案数查找完毕后,还剩下不包含特殊0的位,他们的情况数几乎是可以任意放,也就是pow2[i],但是不能存在全是1的情况,也不能存在只有一个特殊0的情况(会和枚举特殊0的方案数查重),所以需要减去1和i。我们设这个数位tmp2,剩下多少个位,就需要乘上tmp2的几次方。
整理一下,会得到这个式子:
C[n][i]*pow2[(n-1]*(m-1)]*求和符号t=1~m-1[C[m-1][t]*dp[i][t]*tmp2^(m-1-t)]
用第A题的答案去掉长度为1的答案-这里算出来的答案并取模就能得到最终答案。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M 5005
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
ll n,m,q;
ll C[6000][6000] = { 0 };
void calC()
{
C[0][0]=1;
for(int i=1; i<M; i++)
{
for(int j=0; j<=i; j++)
{
if(!j)C[i][j]=1;
else C[i][j]=(C[i-1][j-1]+C[i-1][j])%q;
}
}
}
ll dp[M][M];
ll dp2[M][M];
ll pow2[M*M];
void solve()
{
cin>>n>>m>>q;
calC();
ll ans=0;
ll p=spid(2,m-1,q);///剩余数字的随机状态数
for(int i=2; i<=n; i++)
{
ll t=1;
ll x=spid(2,i,q)-1+q;///每一位不能同时为1
///那么数量就是2^i-1
x%=q;
x=spid (x,m-1,q);///总共还剩m-1位
t=t*x%q;
t=t*spid(p,n-i,q)%q*C[n][i]%q;
ans=ans+t;
///t*spid(p,n-i,q)是剩余点随机状态
///c[n]/(c[i]*c[n-i])是从n个数里面挑i个位置给合法序列
ans%=q;
}
pow2[0] = 1;
for (int i = 1; i < 5005*5005; i++)
{
pow2[i]=pow2[i-1]*2%q;
}
dp2[0][0]=1%q;
for (int i=1; i<=n;i++)
{
for (int j=1;j<=m;j++)
{
dp2[i][j]=(dp2[i-1][j-1]+dp2[i][j - 1])%q*i;
dp2[i][j]%=q;
}
}///枚举i个数分配j个特殊0的情况数
ll del=0;///应该减去的只有一个合法子序列的情况数
for (int i=2; i<=n; i++)
{
///枚举独一无二的零的数量
ll t2=pow2[i]-i-1;
vector<ll>P(m + 1, 1);
for(int j = 1; j <= m; j++){
P[j] = P[j - 1] * t2 %q;
}
ll tem=pow2[(n - i)*(m - 1)]%q*C[n][i]%q;
for (int t=m-1; t>=i; t--) ///特殊位
{
del+=dp2[i][t]*P[m-1-t]%q*tem%q*C[m-1][t]%q;
del%=q;
}
///现在的sum是这几个奇数特殊的状态数
///pow2是其余偶数的任意状态数
///C[n][i]是从n个数拿出i个位置给奇数
}
cout<<((ans-del)%q+q)%q<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
D:(树状数组)
小知识:遇到不认识的模数,需要判断是不是质数(是否可以用逆元求解,否则尝试因子拆分,找出其特别性(例如,是k^w)。就比如A题在写的时候一开始尝试用逆元进行求解,还根据模数不一定是质数,特地使用的扩展欧几里得算法求逆元,但得到的结果确实几发wa,原因就是不是所有数对某个模数,都是拥有属于自己的逆元的(扩展欧几里得算法中有提及如果不存在逆元,返回值是什么)。
解题入手:对于这个特殊的模数,我们通过莫名其妙的方法,发现它是2^21 ,也就是说,对于后缀和2^21后面的部分不用考虑,又因为每插入一个数,会改变所有的所有后缀和。因为我们可以尝试维护前缀和,如何维护呢,会发现题目所给的是求所有后缀和的疑惑和,并且还给了2^21这么个特别的数字,所以我们可以维护单独所有后缀和的每一个位数的。可以用树状数组或者线段树实现(很明显树状数组更适合这份工作,因为只涉及到了累加)。最后得到的答案明显也可以按位查询实现21位,从而得到你要的sum。复杂的点在于前缀和的二进制怎么转化成后缀和的二进制。就好了