[72] (多校联训) A层冲刺NOIP2024模拟赛25
用了 bitset,复杂度比较悬以及完全没用到题目里提示的 __builtin_popcountll()
,所以有点心里没底
最重要的是最后三分钟拍子挂了,给我吓一跳,后来检查发现是 Linux 莫名其妙的问题,答案文件没更新
首先原问题可以转化到一个
对给定的序列
由于只能连
#include<bits/stdc++.h>
using namespace std;
int n,m;
bitset<10001>ans[10001];
string S;
bitset<10001>s,t,r;
int main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
cin>>S;S=" "+S;
for(int i=1;i<=n;++i){
s[i]=((S[i]-'0')>>1)&1;
t[i]=(S[i]-'0')&1;
r[i]=1;
}
for(int i=1;i<=n;++i){
r[i]=0;
if(s[i] and t[i]){
ans[i]^=((t&r)|(s&r));
}
else if(t[i]){
ans[i]^=(s&r);
}
else if(s[i]){
ans[i]^=(t&r);
}
}
}
int anss=0;
for(int i=1;i<=n;++i){
ans[i][i]=0;
anss+=ans[i].count();
}
cout<<anss<<'\n';
}
考虑把所有数都转成乘法
- 对于乘法,显然不用变
- 对于加法
,贪心地想,当我们选到这个加法操作时,其余形如 的 更大的操作肯定已经被选了,因此这个操作会将值改变成 ,其中 是所有值大于 的值的和(这里要注意相等的情况,钦定一个严格的大小关系就好了) - 对于
,由于可能会有很多赋值操作,为了尽量减少损失,肯定是将赋值操作放最前面(避免覆盖掉加和乘的贡献),因此,当放了一个最大的覆盖操作的时候,再放较小的覆盖操作就没有影响了,比如现在有两个覆盖操作 ,可以先用 再用 ,只要最大的在最后,结果就不变,因此不是最大值的覆盖操作可以直接忽略,对于那些是最大值的覆盖操作,由于可以看做是初始值改变,即
这样就可以直接按值排序了
写出来以后首先是被卡了 __int128
其次被卡了对
我的写法是,当遇到形如
然后又被精度卡了,需要写分数类
#include<bits/stdc++.h>
using namespace std;
#define int __int128
const int p=1e9+7;
int power(int a,int t){
int base=a,ans=1;
while(t){
if(t&1){
ans=ans*base%p;
}
base=base*base%p;
t>>=1;
}
return ans;
}
int n,m;
int a[100001];
struct ope_t{
int z,m,id;
bool operator<(const ope_t&A)const{
return z*A.m>A.z*m;
}
};
vector<ope_t>v;
vector<int>add[100001];
int maxn[100001];
ope_t st[100001];
signed main(){
cin>>n>>m;
memset(maxn,-1,sizeof maxn);
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=m;++i){
int opt,x,y;
cin>>opt>>x>>y;
if(opt==1){
maxn[x]=max(maxn[x],y);
}
else if(opt==2){
add[x].push_back(y);
}
else{
v.push_back({y,1,x});
}
}
for(int i=1;i<=n;++i){
if(maxn[i]>a[i]) add[i].push_back(maxn[i]-a[i]);
sort(add[i].begin(),add[i].end(),greater<int>());
int sum=a[i];
for(int j:add[i]){
v.push_back({sum+j,sum,i});
sum+=j;
}
}
sort(v.begin(),v.end());
int ans=1;
for(int i=1;i<=n;++i){
ans=ans*a[i]%p;
}
cout<<ans;putchar(' ');
int unfcnt=0;
for(ope_t i:v){
if(ans*i.z%p*power(i.m,p-2)%p!=0){
ans=ans*i.z%p*power(i.m,p-2)%p;
cout<<ans;
}
else if(i.z%p==0){
st[i.id]={i.z,i.m,i.id};
cout<<0;
unfcnt++;
}
else if(i.m%p==0){
if(st[i.id].id){
ans=ans*i.z%p*power(st[i.id].m,p-2)%p;
st[i.id]={};
}
unfcnt--;
if(!unfcnt) cout<<ans;
else cout<<'0';
}
putchar(' ');
}
for(int i=1;i<=m-(int)v.size();++i){
cout<<ans;putchar(' ');
}
cout<<endl;
}
先考虑怎么处理询问
用给定排列
由于
因此我们需要动态地维护每队数在原序列中出现的次数,注意到这个东西是有结合律的,而且要求支持区间修改,因此直接上线段树,线段树节点里暴力维护
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
string S;
namespace stree{
struct tree{
int cnt[10][10]; //记录区间 [l,r+1] 内相邻的值为 [i,j] 的对数
int lazy;
int lc,rc;
tree operator+(const tree&A)const{
tree ans;
for(int i=0;i<=k-1;++i){
for(int j=0;j<=k-1;++j){
ans.cnt[i][j]=cnt[i][j]+A.cnt[i][j];
}
}
ans.lc=lc;
ans.rc=A.rc;
ans.cnt[rc][A.lc]++;
return ans;
}
}t[200001*4];
#define tol (id*2)
#define tor (id*2+1)
#define mid(l,r) mid=((l)+(r))/2
void update(int id){
// cout<<"update "<<id<<endl;
for(int i=0;i<=k-1;++i){
for(int j=0;j<=k-1;++j){
t[id].cnt[i][j]=t[tol].cnt[i][j]+t[tor].cnt[i][j];
}
}
t[id].lc=t[tol].lc;
t[id].rc=t[tor].rc;
// cout<<t[tol].rc<<' '<<t[tor].lc<<endl;
// assert(t[tol].rc>=0 and t[tol].rc<=k and t[tor].lc>=0 and t[tor].lc<=k);
t[id].cnt[t[tol].rc][t[tor].lc]++;
}
void build(int id,int l,int r){
// cout<<"build "<<id<<" "<<l<<" "<<r<<endl;
// assert(id!=0);
if(l==r){
t[id].lc=t[id].rc=S[l]-'a';
return;
}
int mid(l,r);
build(tol,l,mid);
build(tor,mid+1,r);
update(id);
}
void actlazy(int id,int lazy){
tree ans;
for(int i=0;i<=k-1;++i){
for(int j=0;j<=k-1;++j){
// cout<<" "<<i<<" "<<lazy<<endl;
// if(i+lazy<0) cout<<i<<" "<<lazy<<" when actlazy "<<id<<endl;
// assert((i+lazy)>=0);
ans.cnt[(i+lazy)%k][(j+lazy)%k]=t[id].cnt[i][j];
}
}
ans.lc=t[id].lc;
ans.rc=t[id].rc;
ans.lazy=t[id].lazy;
t[id]=ans;
}
void pushdown(int id){
if(t[id].lazy){
t[tol].lazy+=t[id].lazy;
t[tor].lazy+=t[id].lazy;
t[tol].lc=(t[tol].lc+t[id].lazy)%k;
t[tor].lc=(t[tor].lc+t[id].lazy)%k;
t[tol].rc=(t[tol].rc+t[id].lazy)%k;
t[tor].rc=(t[tor].rc+t[id].lazy)%k;
// if(tol==18) cout<<"actlazyl "<<tol<<" "<<t[id].lazy<<" of "<<id<<endl;
actlazy(tol,t[id].lazy);
// if(tol==18) cout<<"actlazyr "<<tor<<" "<<t[id].lazy<<endl;
actlazy(tor,t[id].lazy);
t[id].lazy=0;
}
}
void change(int id,int l,int r,int L,int R,int val){
// cout<<"change "<<id<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<" "<<val<<endl;
if(L<=l and r<=R){
// if(id==9) cout<<t[id].lazy<<"+="<<val<<endl;
t[id].lazy+=val;
t[id].lc=(t[id].lc+val)%k;
t[id].rc=(t[id].rc+val)%k;
// if(id==18) cout<<"actlazy "<<id<<" "<<val<<endl;
actlazy(id,val);
return;
}
int mid(l,r);
pushdown(id);
if(R<=mid) change(tol,l,mid,L,R,val);
else if(L>=mid+1) change(tor,mid+1,r,L,R,val);
else{
change(tol,l,mid,L,mid,val);
change(tor,mid+1,r,mid+1,R,val);
}
update(id);
}
tree ask(int id,int l,int r,int L,int R){
if(L<=l and r<=R){
return t[id];
}
pushdown(id);
int mid(l,r);
if(R<=mid) return ask(tol,l,mid,L,R);
else if(L>=mid+1) return ask(tor,mid+1,r,L,R);
return ask(tol,l,mid,L,mid)+ask(tor,mid+1,r,mid+1,R);
}
}
signed main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
// freopen("/home/hdk/code/11.21/down/d/d2.in","r",stdin);
// freopen("out.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>m>>k;
// cout<<":::"<<n<<" "<<m<<" "<<k<<endl;
cin>>S;S=" "+S;
stree::build(1,1,n);
while(m--){
int opt,l,r;cin>>opt>>l>>r;
if(opt==1){
int c;cin>>c;
stree::change(1,1,n,l,r,c);
}
else{
string t;cin>>t;
stree::tree tmp=stree::ask(1,1,n,l,r);
int ans=0;
for(int i=0;i<=(int)t.length()-1;++i){
for(int j=0;j<=i;++j){
ans+=tmp.cnt[t[i]-'a'][t[j]-'a'];
}
}
cout<<ans+1<<'\n';
}
}
}
这是什么
放点福瑞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!