给定长度为nn的数组, 定义数字XX在[l,r][l,r]内的值为数字XX在[l,r][l,r]内最后一次出现位置的下标减去第一次出现位置的下标
给定mm次询问, 每次询问有三个整数a, b, ca,b,c,询问规则如下:
当a = 1a=1时, 将数组内第bb个元素更改为cc
当a = 2a=2时, 求区间[b,c][b,c]所有数字的值的和
输入:
第一行两个整数n,mn,m
第二行nn个整数, 表示数组
第3-3 + m3−3+m行, 每行三个整数, 表示每次询问
输出:
对于每次a = 2a=2的询问, 输出一个整数表示答案
题解:对时间CDQ分治,把每一个数的权值设为pos-pre,坐标看作(i,pre)
这样就是一个静态2维数点问题,扫描线+树状数组即可
代码:(写得比较烂。。)
#include
#include
#include
#include
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
#define M 800005
#define LL long long
int n,m;
set S[N];
set::iterator it,it2;
struct node{
int op,l,r,x,y,id;
node(){}
node(int a,int b,int c,int d,int e,int f){op=a;l=b;r=c;x=d;y=e;id=f;}
//op=1 l,r,x
//op=2 l,r,x,y,id l,x,y,flg(r)
bool operator < (const node &t)const{
return l>1;
solve(l,mid);solve(mid+1,r);
int qcnt=0;
for(int i=l;i<=mid;i++)
if(e[i].op==1)q[++qcnt]=e[i];
int ff=0;
for(int i=mid+1;i<=r;i++)
if(e[i].op==2){
ff++;
q[++qcnt]=e[i];q[qcnt].r=-1;
q[++qcnt]=e[i];q[qcnt].l=q[qcnt].r;q[qcnt].r=1;
}
if(!ff)return;
sort(q+1,q+qcnt+1);
for(int i=1;i<=qcnt;i++){
if(q[i].op==1)update(q[i].r,q[i].x);
else ans[q[i].id]+=getsum(q[i].x,q[i].y)*q[i].r;
}
for(int i=1;i<=qcnt;i++)
if(q[i].op==1)update(q[i].r,-q[i].x);
}
int main()
{
int i,op,l,r,p,x;
n=gi();m=gi();
for(i=1;i<=n;i++)S[i].insert(0);
for(i=1;i<=n;i++){a[i]=gi();S[a[i]].insert(i);}
for(i=1;i<=n;i++){
it=S[a[i]].lower_bound(i);it--;
e[++tot]=node(1,i,*it,i-(*it),0,0);
}
int acnt=0;
for(i=1;i<=m;i++){
op=gi();
if(op==1){
p=gi();x=gi();
it2=it=S[a[p]].lower_bound(p);it2--;it++;
if(it!=S[a[p]].end()){
e[++tot]=node(1,*it,p,p-*it,0,0);
e[++tot]=node(1,*it,*it2,*it-*it2,0,0);
}
e[++tot]=node(1,p,*it2,*it2-p,0,0);
S[a[p]].erase(p);
a[p]=x;
S[a[p]].insert(p);
it2=it=S[a[p]].lower_bound(p);it2--;it++;
if(it!=S[a[p]].end()){
e[++tot]=node(1,*it,*it2,*it2-*it,0,0);
e[++tot]=node(1,*it,p,*it-p,0,0);
}
e[++tot]=node(1,p,*it2,p-*it2,0,0);
}
else{
l=gi();r=gi();
e[++tot]=node(2,l-1,r,l,r,++acnt);
}
}
solve(1,tot);
for(i=1;i<=acnt;i++)
printf("%lld\n",ans[i]);
}
题解:动态开点线段Trie树(大概也只适用于这道题吧)
用BFS从高到低确定答案的某一位,同时在两棵线段Trie树上移动节点
当某一个节点被完全标记了,此时答案中低位的所有情况都可以被取到
那么这一部分的答案就是等差数列求和,首项就是已经确定了的高位的部分
复杂度应该是O(n*log10^18)
代码:
#include
#include
#include
#include
using namespace std;
#define N 1000005
#define LOG 60
#define LL long long
inline LL gi()
{
char c;LL num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+1ll*c-48;c=getchar();}
return num*flg;
}
const int mod=998244353;
const int inv2=499122177;
struct SegTrie{
int ch[N][2],tot,rt;
bool flg[N];
void insert(int &i,LL l,LL r,LL ql,LL qr){
if(!i)i=++tot;
if(ql==l&&r==qr){flg[i]=1;return;}
LL mid=(l+r)>>1;
if(qlmid)insert(ch[i][1],mid,r,ql>mid?ql:mid,qr);
}
}A,B;
vector >q[LOG+5];
LL BFS(int d,LL sum)
{
for(int i=0;i<(int)q[d].size();i++)
if(A.flg[q[d][i].first]||B.flg[q[d][i].second])
return (sum+1ll*((1ll<
题意: 给一个图,带加边,删边,保证图联通,求从1号点出发异或值最大的回路
题解:线段树分治+bitset线性基+带权并查集
板题,没什么好说的
代码:
#include
#include
#include
#include
#include
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 1003
#define BS bitset
struct LBS{
BS bas[N];
void insert(BS x){
for(int i=1000;i>=0;i--)if(x[i]){
if(bas[i].none()){bas[i]=x;return;}
x=x^bas[i];
}
}
BS getmax(){
BS ret;
for(int i=1000;i>=0;i--)if(!ret[i])
if(!bas[i].none())ret=ret^bas[i];
return ret;
}
}empt;
struct edge{
int u,v;BS w;
}tmp,E[N];
#define lc i<<1
#define rc i<<1|1
struct node{
int l,r;
vector x;
}a[N<<2];
void build(int i,int l,int r)
{
a[i].l=l;a[i].r=r;
if(l==r)return;
int mid=(l+r)>>1;
build(lc,l,mid);build(rc,mid+1,r);
}
void insert(int i,int l,int r)
{
if(a[i].l>r||a[i].r=0;i--)if(x[i])break;
if(i>=0)for(;i>=0;i--)if(x[i])printf("1");else printf("0");
printf("\n");
}
int fa[N],H[N];BS dis[N],zer;
int stk[10*N],top;
int find(int x){while(x!=fa[x])x=fa[x];return x;}
BS getdis(int x){BS ret;while(x!=fa[x])ret=ret^dis[x],x=fa[x];return ret;}
void back(int tmp)
{
while(top>tmp){
if(stk[top]<0)H[-stk[top]]--;
else dis[stk[top]]=zer,fa[stk[top]]=stk[top];
top--;
}
}
void solve(int i,LBS now)
{
int tmptop=top;
for(int j=0;j<(int)a[i].x.size();j++){
int u=a[i].x[j].u,v=a[i].x[j].v;
int p=find(u),q=find(v);
BS w=a[i].x[j].w^getdis(u)^getdis(v);
if(p==q)now.insert(w);
else{
if(H[p]
题意:一个图,带加边删边,判断此图是否为二分图
题解:线段树分治+带权并查集
板题
代码:
#include
#include
#include
#include