CodeForces:1227&1261(div1)&1262(div2)

前言

三倍经验
(为了方便,均按洛谷题号)
1227ABC是水题
D是小清新数据结构
E是之前考过的题,但是没了不碰边界的条件,细节稍微要更多一些
F1是垃圾dp,F2是降智数学题(水紫!)
G是很神奇的构造题
1261F是神仙线段树题,实现起来倒不难,主要是难以想到

CF1227A Math Problem

Description \text{Description} Description

给出 n n n 条线段(含端点),请你给出一条最短的线段,使其与所有线段都有交,输出最短的长度( r − l r-l rl).
n ≤ 1 0 5 n\le10^5 n105

Solution \text{Solution} Solution

要求均有交的充要条件是: l ≤ min ⁡ r i ∧ r ≥ max ⁡ l i l\le\min r_i\land r\ge\max l_i lminrirmaxli.
所以答案就是 max ⁡ ( 0 , max ⁡ l i − min ⁡ r i ) \max(0,\max l_i-\min r_i) max(0,maxliminri).

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=5e5+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;

signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  int T=read();
  while(T--){
    int l=2e9,r=-2e9;
    n=read();
    for(int i=1;i<=n;i++){
      int x=read(),y=read();
      l=min(l,y);r=max(r,x);
    }
    r=max(l,r);
    printf("%d\n",r-l);
  }
  return 0;
}
/*

*/

CF1227B Box

Description \text{Description} Description

有一个长度为 n n n 的排列 q 1... n q_{1...n} q1...n,定义 p i = max ⁡ j ≤ i q j p_i=\max_{j\le i} q_j pi=maxjiqj,现在给出 p 1... n ( p i ≥ p i − 1 ) p_{1...n}(p_i\ge p_{i-1}) p1...n(pipi1),请你构造一个合法的 q q q 或者报告无解.
n ≤ 1 0 5 n\le 10^5 n105

Solution \text{Solution} Solution

显然,最大值改变的位置必然就是最大值.
剩下的位置从前往后贪心的先填小的即可.
如果和最大值条件矛盾则无解.

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
int vis[N];
int ans[N],mx[N];
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  int T=read();
  while(T--){
    int now=0;
    n=read();
    fill(vis,vis+1+n,0);
    fill(ans,ans+1+n,0);
    for(int i=1;i<=n;i++){
      mx[i]=read();
      if(mx[i]>now){
	now=mx[i];ans[i]=mx[i];vis[mx[i]]=1;
	//printf("i=%d mx=%d\n",i,mx[i]);
      }
    }
    int pl=1,flag=0;
    for(int i=1;i<=n;i++){
      if(ans[i]) continue;
      while(vis[pl]) ++pl;
      //printf("i=%d pl=%d\n",i,pl);
      ans[i]=pl;vis[pl]=1;
      if(ans[i]>mx[i]){
	flag=1;printf("-1\n");break;
      }
    }
    if(!flag){
      for(int i=1;i<=n;i++) printf("%d ",ans[i]);
      putchar('\n');
    }    
  }
  return 0;
}
/*

*/

CF1227C Messy

Description \text{Description} Description

给出一个长度为 n n n 的括号序列,你每次可以翻转一个区间,请在 n n n 次操作内使序列变为合法序列且恰好有 k k k 个前缀是合法前缀(包括本身).
保证存在合法解.
n ≤ 2000 n\le2000 n2000

Solution \text{Solution} Solution

令前 k − 1 k-1 k1 个都是 ( ) () () 的形状,然后填成 ( ( ( ( ( . . . ) ) ) ) ) (((((...))))) (((((...))))) 即可.
n 2 n^2 n2 暴力就可过.
(预处理一些东西应该可以线性,但是懒得打了).

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
char s[N];
int l,r;
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  int T=read();
  while(T--){
    n=read();k=read();
    scanf(" %s",s+1);
    --k;int x=n/2,y=n/2;
    printf("%d\n",n);
    for(int i=1;i<=n;i++){
      if((k>0&&i%2==0)||(!x)){
	l=r=i;
	while(s[r]=='(') ++r;
	printf("%d %d\n",l,r);
	swap(s[l],s[r]);
	--k;--y;
      }
      else{
	l=r=i;
	while(s[r]==')') ++r;
	printf("%d %d\n",l,r);
	swap(s[l],s[r]);
	--x;
      }
    }
  }
  return 0;
}
/*

*/

CF1227D Optimal Subsequences

Description \text{Description} Description

给定一个长度为 n n n 的正整数序列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an

m m m 个询问,每次询问给出两个正整数 k , p o s k,pos k,pos。你需要找到一个长度为 k k k子序列,且满足如下要求:

  • 该子序列的元素和是所有子序列中最大的;
  • 该子序列是所有满足上一条件的子序列中字典序最小的一个。

对于每个询问,输出该子序列的第 p o s pos pos 个元素的值。

1 ≤ n , m ≤ 2 × 1 0 5 1 \le n,m \le 2\times10^5 1n,m2×105(这是与困难版本唯一的区别),   1 ≤ k ≤ n \ 1 \le k \le n  1kn,在同一询问中有 1 ≤ p o s ≤ k 1 \le pos \le k 1posk

Solution \text{Solution} Solution

一种主席树实现的在线做法.
设选出的子序列的最小值为 m n mn mn,那么必然是大于 m n mn mn 的全部选,等于 m n mn mn 的元素尽量靠前选.
m n mn mn 在当前序列中共出现了 n u m num num 次.
考虑二分答案,利用主席树询问前面在给定能否选到的数的个数是否达到 k k k 次,注意 m n mn mn 最多取 n u m num num 个.

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
int a[N],q[N],cnt,sum[N],ori[N];
#define mid ((l+r)>>1)
struct tree{
  int siz,ls,rs;
}tr[N<<5];
int rt[N],tot;
inline int copy(int x){
  tr[++tot]=tr[x];return tot;
}
void upd(int &k,int l,int r,int p){  
  k=copy(k);
  //debug("k=%d (%d %d) p=%d\n",k,l,r,p);
  if(l==r){
    tr[k].siz++;return;
  }
  if(p<=mid) upd(tr[k].ls,l,mid,p);
  else upd(tr[k].rs,mid+1,r,p);
  tr[k].siz=tr[tr[k].ls].siz+tr[tr[k].rs].siz;
  return;
}
int ask(int k,int l,int r,int x,int y){
  if(!k||x>y) return 0;
  if(x<=l&&r<=y) return tr[k].siz;
  int res=0;
  if(x<=mid) res+=ask(tr[k].ls,l,mid,x,y);
  if(y>mid) res+=ask(tr[k].rs,mid+1,r,x,y);
  return res;
}
bool cmp(int x,int y){return x>y;}
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();
  for(int i=1;i<=n;i++) q[i]=a[i]=read();
  sort(q+1,q+1+n);
  cnt=unique(q+1,q+1+n)-q-1;
  for(int i=1;i<=n;i++) a[i]=lower_bound(q+1,q+1+cnt,a[i])-q;
  for(int i=1;i<=n;i++){
    sum[a[i]]++;
    rt[i]=rt[i-1];
    upd(rt[i],1,cnt,a[i]);
  }
  for(int i=cnt;i>=1;i--) sum[i]+=sum[i+1];
  memcpy(ori,a,sizeof(a));
  sort(a+1,a+1+n,cmp);
  m=read();
  for(int i=1;i<=m;i++){
    int k=read(),pos=read();
    int mn=a[k],num=k-sum[mn+1];
    //printf("mn=%d num=%d\n",mn,num);
    int st=1,ed=n;
    while(st<ed){
      int mmid=(st+ed)>>1;
      if(ask(rt[mmid],1,cnt,mn+1,cnt)+min(num,ask(rt[mmid],1,cnt,mn,mn))>=pos) ed=mmid;
      else st=mmid+1;	    
    }
    printf("%d\n",q[ori[st]]);
  }
  return 0;
}
/*

*/

CF1227E Arson In Berland Forest

Description \text{Description} Description

在一个无限大的矩阵中,每个位置是一棵树。在一个 n × m n \times m n×m 的子矩阵中,发生了一场火灾,一些树被摧毁了。被摧毁的树用字符 X 表示,未被摧毁的树用字符 . 表示。子矩阵外的树都没有被摧毁.

  • 0 0 0 时刻,有些树是自发着火的.
  • 接下来的每一分钟内,每一棵着火(也即,被摧毁)的树,会使得它周围的 8 8 8 个相邻的树着火.
  • 在第 T T T 分钟初,火灾停止.

现在给定最后被摧毁的树,求最大可能的 T T T,并求出任意一种满足的、自发着火的树的集合.

Solution \text{Solution} Solution

bfs 预处理出每个燃烧点到外部的最短距离 d i s dis dis.
二分时间 t t t,那么 d i s ≥ t dis\ge t dist 的点是可以初始燃烧的.
从可以初始燃烧的所有点 bfs t t t 次,若 bfs 的地图和原图一致,则合法.
注意边界的树是完好的.

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=3e6+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
#define id(a,b) ((a)*(m+2)+(b))
bool vis[N];
int mp[N],dis[N];
int dx[9]={0,-1,-1,-1,0,1,1,1,0},dy[9]={0,-1,0,1,1,1,0,-1,-1};
#define pr pair<int,int>
#define mkp make_pair
pr q[N];
int st,ed;
void bfs(){
  st=1;ed=0;
  for(int i=0;i<=n+1;i++){
    for(int j=0;j<=m+1;j++){
      if(!mp[id(i,j)]){
	vis[id(i,j)]=1;q[++ed]=mkp(i,j);
      }
    }
  }
  while(st<=ed){
    int x=q[st].first,y=q[st].second;++st;
    //printf("(%d %d)\n",x,y);
    for(int i=1;i<=8;i++){
      int xx=x+dx[i],yy=y+dy[i];
      if(xx<1||xx>n||yy<1||yy>m) continue;
      if(vis[id(xx,yy)]) continue;
      //printf("  ->(%d %d)\n",xx,yy);
      vis[id(xx,yy)]=1;dis[id(xx,yy)]=dis[id(x,y)]+1;
      q[++ed]=mkp(xx,yy);
    }
  }
  return;
}
int a[N];
bool check(int k){
  st=1,ed=0;
  memset(vis,0,sizeof(vis));
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++) if(dis[id(i,j)]>=k) q[++ed]=mkp(i,j),vis[id(i,j)]=1;
  }
  //for(int i=1;i<=n;i++){
  //for(int j=1;j<=m;j++) printf("%d",vis[id(i,j)]);
  //putchar('\n');
  //}
  for(int ned=ed,i=1;i<k;i++,ed=ned){
    while(st<=ed){
      int x=q[st].first,y=q[st].second;++st;
      for(int i=1;i<=8;i++){
	int xx=x+dx[i],yy=y+dy[i];
	if(xx<1||xx>n||yy<1||yy>m) return false;
	if(vis[id(xx,yy)]) continue;
	vis[id(xx,yy)]=1;
	q[++ned]=mkp(xx,yy);
      }
    }
  }
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++) if(vis[id(i,j)]^mp[id(i,j)]) return false;
  }
  return true;
}
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();m=read();
  char c;
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
      scanf(" %c",&c);
      mp[id(i,j)]=c=='X';
    }
  }
  bfs();
  //printf("%d\n",check(2));
  //return 0;
  int st=1,ed=max(n,m);
  while(st<ed){
    int mid=(st+ed+1)>>1;
    if(check(mid)) st=mid;
    else ed=mid-1;
  }  
  printf("%d\n",st-1);
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
      if(dis[id(i,j)]>=st) putchar('X');
      else putchar('.');
    }
    putchar('\n');
  }
  return 0;
}
/*
*/

CF1227F Wrong Answer on test 233

Description \text{Description} Description

n n n 道题,你的程序在上交答案时把答案交串了,第 i i i 个答案变成了第 i % n + 1 i\%n+1 i%n+1 个.
给出 n n n 道题的正确答案以及选项数 k k k ,求有多少中初始上交的答案,能使交串后正确的题目数变多.

Solution \text{Solution} Solution

easy version

n ≤ 2000 , k ≤ 1 0 9 n\le2000,k\le10^9 n2000,k109
设计 d p i , j dp_{i,j} dpi,j 表示填到第 i i i 题,多填对了 j j j 道题的方案数.
暴力转移即可.

hard version

n ≤ 2 × 1 0 5 , k ≤ 1 0 9 n\le2\times10^5,k\le10^9 n2×105,k109
原来 dp 的做法行不通了,我们需要另辟蹊径.
f i f_i fi 表示多对了 i i i 道题的方案数,由于对称性,有 f i = f − i f_i=f_{-i} fi=fi.
所以我们的答案其实就是:
k n − f 0 2 \dfrac{k^n-f_0}{2} 2knf0
所以我们只需要求出 f 0 f_0 f0 就行了.
连续两个答案相同无法产生差异,我们扫一遍求出有 m m m 个位置可以产生差异.
枚举答对(答错)的题目数,则有:
f 0 = ∑ i = 0 ⌊ m 2 ⌋ k n − m × C m i × C m − 1 i × ( k − 2 ) m − 2 i f_0=\sum_{i=0}^{\lfloor\frac{m}{2}\rfloor}k^{n-m}\times C_m^i\times C_{m-1}^i\times (k-2)^{m-2i} f0=i=02mknm×Cmi×Cm1i×(k2)m2i
直接求解即可.

Code \text{Code} Code

dp:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2050;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
ll dp[2][N<<1];
int now,nxt,o=2000,a[N];
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();k=read();
  for(int i=1;i<=n;i++) a[i]=read();
  dp[nxt=1][0+o]=1;
  for(int i=1;i<=n;i++){
    swap(now,nxt);
    memset(dp[nxt],0,sizeof(dp[nxt]));
    for(int j=-i;j<=i;j++){
      if(a[i]==a[i%n+1]) (dp[nxt][j+o]+=dp[now][j+o]*k)%=mod;
      else{
	(dp[nxt][j+1+o]+=dp[now][j+o])%=mod;
	(dp[nxt][j-1+o]+=dp[now][j+o])%=mod;
	(dp[nxt][j+o]+=(k-2)*dp[now][j+o])%=mod;
      }
    }
  }
  ll res(0);
  for(int i=1;i<=n;i++) (res+=dp[nxt][i+o])%=mod;
  printf("%lld\n",res);
  return 0;
}
/*
*/

组合数:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
int a[N];
ll ans;
ll ksm(ll x,ll k){
  ll res=1;
  while(k){
    if(k&1) res=res*x%mod;
    x=x*x%mod;
    k>>=1;
  }
  return res;
}
ll jc[N],ni[N],mi[N];
inline ll C(int n,int m){
  return jc[n]*ni[m]%mod*ni[n-m]%mod;
}
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();k=read();
  if(k==1){
    printf("0");return 0;
  }
  for(int i=1;i<=n;i++) a[i]=read();
  for(int i=1;i<=n;i++) m+=(a[i]!=a[i%n+1]);
  jc[0]=1;
  for(int i=1;i<=m;i++) jc[i]=jc[i-1]*i%mod;
  ni[m]=ksm(jc[m],mod-2);
  for(int i=m-1;i>=0;i--) ni[i]=ni[i+1]*(i+1)%mod;
  mi[0]=1;
  for(int i=1;i<=n;i++) mi[i]=mi[i-1]*(k-2)%mod;
  ans=ksm(k,n);ll w=ksm(k,n-m);
  for(int i=0;i<=m/2;i++) ans=(ans+mod-w*C(m,i)%mod*C(m-i,i)%mod*mi[m-2*i]%mod)%mod;
  ans*=ksm(2,mod-2);ans%=mod;
  printf("%lld\n",ans);
  return 0;
}
/*
*/

CF1227G Not Same

Description \text{Description} Description

给定大小为 n n n 的序列 a a a , 满足 1 ⩽ a i ⩽ n 1 \leqslant a_i \leqslant n 1ain.

你需要执行至多 n + 1 n+1 n+1 次操作使得所有数变为 0 0 0 ,每次操作你可以把一个子集的元素都 − 1 -1 1 , 要求每次操作的子集互不相同.

n ⩽ 1000 n\leqslant 1000 n1000.

Solution \text{Solution} Solution

把所有元素从大到小排序.
排序后的第 i i i 个元素从第 i i i 行开始连续填,到头循环即可.
为什么这样必然合法?
首先不难发现,这样填完之后后一列最多比前一列的下部低一位.
假设第 i i i 行和第 j j j 行完全相同. ( i < j ) (i<j) (i<j)
那么由于第 i i i 行的第 i + 1 i+1 i+1 列没有数,那么第 j j j 行的第 i + 1 i+1 i+1 列也没有数.
那么第 i + 2 i+2 i+2 列最多比 i + 1 i+1 i+1 列往下一行,不可能同时让第 i i i 列和第 j j j 列有数,也只能让两列都没有数.
以此类推,最后到第 j j j 列的时候,第 j j j 行第 j j j 列必然有数(元素为正数),但第 i i i 行第 j j j 列却没有数,矛盾.
原命题得证.

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1050;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
int a[N],x[N];
bool cmp(int x,int y){return a[x]>a[y];}
int ans[N][N];
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();
  for(int i=1;i<=n;i++) a[i]=read(),x[i]=i;
  sort(x+1,x+1+n,cmp);
  for(int i=1;i<= n;i++){
    int now=x[i];
    for(int j=i;a[now];j=j%(n+1)+1) ans[j][now]=1,a[now]--;
  }
  printf("%d\n",n+1);
  for(int i=1;i<=n+1;i++){
    for(int j=1;j<=n;j++) printf("%d",ans[i][j]);
    putchar('\n');
  }
  return 0;
}
/*
*/

CF1261F Xor-Set

Description \text{Description} Description

给出两个整数集合 A , B A,B A,B,求所有使得存在 a ∈ A , b ∈ B a \in A,b \in B aA,bB a ⊕ b = c a \oplus b=c ab=c 的整数 c c c 之和取模 998244353 998244353 998244353 的值,其中 ⊕ \oplus 表示按位异或,即 xor.

对于 A A A,给出 n A n_A nA,然后给出 n A n_A nA 个区间 [ l i , r i ] [l_i,r_i] [li,ri],表示对于所有满足 l i ≤ a ≤ r i l_i \le a \le r_i liari 的整数 a a a 都有 a ∈ A a \in A aA(即 [ l i , r i ] ∈ A [l_i,r_i] \in A [li,ri]A)。对于 B B B 以同样方式给出其中的元素.

A , B A,B A,B 都是不重集,即每个整数至多在集合中出现一次(虽然对题意没有影响).

数据范围: 1 ≤ n A , n B ≤ 100 , 1 ≤ l i , r i ≤ 1 0 18 1 \le n_A,n_B \le 100,1 \le l_i,r_i \le 10^{18} 1nA,nB100,1li,ri1018.

Solution \text{Solution} Solution

每个区间可以转化为 O ( l o g n ) O(logn) O(logn) 个形如 [ l , l + 2 k − 1 ] [l,l+2^k-1] [l,l+2k1] 的连续区间.
注意到,当两个这样的区间异或时,结果还是一个连续区间,而且易于求解.
直接拆的话复杂度是 O ( n 2 l o g 2 1 0 18 ) O(n^2log^210^{18}) O(n2log21018).
考虑优化.
注意到,若长度为 x x x 的区间 A A A 与 长度为 y y y 的区间 B B B 异或( x ≥ y ) x\ge y) xy),结果等价于区间 A A A 与包含区间 B B B 的长度同样为 x x x 的区间异或.
所以我们上线段树,每次令一个集合存入所有线段经过的所有区间,另一个集合存入所有线段包含的所有区间,对同一深度的区间异或即可.
这样异或得到的区间数 w = O ( n 2 l o g 1 0 18 ) w=O(n^2log10^{18}) w=O(n2log1018),由于最后求所以区间交需要排序,所以总复杂度为 O ( w l o g w ) O(wlogw) O(wlogw).

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=105;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int na,nb;
ll o=(1ll<<60)-1;
#define mid ((l+r)>>1)
struct line{
  ll l,r;
  bool operator < (const line &o)const{return l<o.l;}
}a[N],b[N];
vector<line>va[80],vb[80];
void upd1(ll l,ll r,ll x,ll y,ll d){
  //printf("  upd1:(%lld %lld) (%lld %lld) d=%lld\n",l,r,x,y,d);
  va[d].push_back((line){l,r});
  if(x<=l&&r
     <=y) return;
  if(x<=mid) upd1(l,mid,x,y,d+1);
  if(y>mid) upd1(mid+1,r,x,y,d+1);
  return;
}
void upd2(ll l,ll r,ll x,ll y,ll d){
  //printf("  upd2:(%lld %lld) (%lld %lld) d=%lld\n",l,r,x,y,d);
  if(x<=l&&r<=y){
    vb[d].push_back((line){l,r});return;
  }
  if(x<=mid) upd2(l,mid,x,y,d+1);
  if(y>mid) upd2(mid+1,r,x,y,d+1);
  return;
}

line ans[N*N*500];
int tot;
void solve(){
  for(int i=1;i<=na;i++) upd1(0,o,a[i].l,a[i].r,0);
  for(int i=1;i<=nb;i++) upd2(0,o,b[i].l,b[i].r,0);
  for(int d=1;d<=60;d++){
    for(auto u:va[d]){
      for(auto v:vb[d]){
	ll low=u.l^u.r;
	ans[++tot]=(line){(u.l^v.l)&(~low),(u.l^v.l)|low};
	//printf("(%lld %lld) ^ (%lld %lld) -> (%lld %lld)\n",u.l,u.r,v.l,v.r,ans[tot].l,ans[tot].r);
      }
    }
  }
  return;
}
ll res;
inline ll sum(ll a,ll b){
  a%=mod;b%=mod;
  return ((b-a+1)*(a+b)>>1)%mod;
}
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  na=read();for(int i=1;i<=na;i++) a[i]=(line){read(),read()};
  nb=read();for(int i=1;i<=nb;i++) b[i]=(line){read(),read()};
  solve();
  for(int i=1;i<=60;i++) va[i].clear();//va[i].shrink_to_fit();
  for(int i=1;i<=60;i++) vb[i].clear();//vb[i].shrink_to_fit();
  swap(na,nb),swap(a,b);//printf("swap------\n");
  solve();
  sort(ans+1,ans+1+tot);
  ll l=ans[1].l,r=ans[1].r;
  //printf("(%lld %lld)\n",l,r);
  for(int i=2;i<=tot;i++){
    if(ans[i].l<=r) r=max(r,ans[i].r);
    else{
      (res+=mod+sum(l,r))%=mod;
      l=ans[i].l,r=ans[i].r;
    }
    //printf("(%lld %lld)\n",ans[i].l,ans[i].r);
  }
  (res+=mod+sum(l,r))%=mod;
  printf("%lld\n",res);
  return 0;
}
/*
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值