文章目录
前言
三倍经验
(为了方便,均按洛谷题号)
1227ABC是水题
D是小清新数据结构
E是之前考过的题,但是没了不碰边界的条件,细节稍微要更多一些
F1是垃圾dp,F2是降智数学题(水紫!)
G是很神奇的构造题
1261F是神仙线段树题,实现起来倒不难,主要是难以想到
CF1227A Math Problem
Description \text{Description} Description
给出
n
n
n 条线段(含端点),请你给出一条最短的线段,使其与所有线段都有交,输出最短的长度(
r
−
l
r-l
r−l).
n
≤
1
0
5
n\le10^5
n≤105
Solution \text{Solution} Solution
要求均有交的充要条件是:
l
≤
min
r
i
∧
r
≥
max
l
i
l\le\min r_i\land r\ge\max l_i
l≤minri∧r≥maxli.
所以答案就是
max
(
0
,
max
l
i
−
min
r
i
)
\max(0,\max l_i-\min r_i)
max(0,maxli−minri).
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=maxj≤iqj,现在给出
p
1...
n
(
p
i
≥
p
i
−
1
)
p_{1...n}(p_i\ge p_{i-1})
p1...n(pi≥pi−1),请你构造一个合法的
q
q
q 或者报告无解.
n
≤
1
0
5
n\le 10^5
n≤105
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
n≤2000
Solution \text{Solution} Solution
令前
k
−
1
k-1
k−1 个都是
(
)
()
() 的形状,然后填成
(
(
(
(
(
.
.
.
)
)
)
)
)
(((((...)))))
(((((...))))) 即可.
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 1≤n,m≤2×105(这是与困难版本唯一的区别), 1 ≤ k ≤ n \ 1 \le k \le n 1≤k≤n,在同一询问中有 1 ≤ p o s ≤ k 1 \le pos \le k 1≤pos≤k。
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
dis≥t 的点是可以初始燃烧的.
从可以初始燃烧的所有点 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
n≤2000,k≤109
设计
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
n≤2×105,k≤109
原来 dp 的做法行不通了,我们需要另辟蹊径.
设
f
i
f_i
fi 表示多对了
i
i
i 道题的方案数,由于对称性,有
f
i
=
f
−
i
f_i=f_{-i}
fi=f−i.
所以我们的答案其实就是:
k
n
−
f
0
2
\dfrac{k^n-f_0}{2}
2kn−f0
所以我们只需要求出
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=0∑⌊2m⌋kn−m×Cmi×Cm−1i×(k−2)m−2i
直接求解即可.
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 1⩽ai⩽n.
你需要执行至多 n + 1 n+1 n+1 次操作使得所有数变为 0 0 0 ,每次操作你可以把一个子集的元素都 − 1 -1 −1 , 要求每次操作的子集互不相同.
n ⩽ 1000 n\leqslant 1000 n⩽1000.
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
a∈A,b∈B 且
a
⊕
b
=
c
a \oplus b=c
a⊕b=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 li≤a≤ri 的整数 a a a 都有 a ∈ A a \in A a∈A(即 [ 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} 1≤nA,nB≤100,1≤li,ri≤1018.
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+2k−1] 的连续区间.
注意到,当两个这样的区间异或时,结果还是一个连续区间,而且易于求解.
直接拆的话复杂度是
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)
x≥y),结果等价于区间
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;
}
/*
*/