1002 three arrays
题意
给定a和b数组,任意排列,使得
c
[
i
]
=
a
[
i
]
X
O
R
b
[
i
]
c[i]=a[i]XOR\space b[i]
c[i]=a[i]XOR b[i]的字典序最小
思路
字典树搞起来,对a和b各建一棵01字典树,对于每一位尽可能取相同的数就能使得异或和最小。这个思路对于很多问题都适用,不仅仅是异或和
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define lson node<<1
#define rson node<<1|1
using namespace std;
typedef long long ll;
const ll inf=2e18;
const ll mod=998244353;
const int maxn = 1e5+20;
const int m=1e9;
int n,tota,totb;
int a[maxn];
int b[maxn];
int ta[40*maxn][2];
int tb[40*maxn][2];
int numa[40*maxn];
int numb[40*maxn];
int ans[maxn];
int cnt;
void init()
{
cnt=0;
tota=totb=1;
ta[0][0]=ta[0][1]=tb[0][0]=tb[0][1]=0;
numa[0]=numb[0]=0;
}
void add(int x,int tree[][2],int num[],int &tot)
{
int u=0;
for(int i=29;i>=0;i--)
{
int v=(x>>i)&1;
if(tree[u][v]==0)
{
tree[tot][0]=tree[tot][1]=0;
num[tot]=0;
tree[u][v]=tot++;
}
u=tree[u][v];
num[u]++;
}
}
void dfs(int x1,int x2,int len,int res,int sum)
{
//cout<<len<<"--"<<endl;
if(len<0)
{
for(int i=1;i<=sum;i++)
{
ans[cnt++]=res;
}
return ;
}
int z=numa[ta[x1][0]],zz=numb[tb[x2][0]];
int w=numa[ta[x1][1]],ww=numb[tb[x2][1]];
if(z>0&&zz>0)
{
int mi=min(z,zz);
z-=mi,zz-=mi;
numa[ta[x1][0]]-=mi;
numb[tb[x2][0]]-=mi;
dfs(ta[x1][0],tb[x2][0],len-1,res,mi);
}
if(w>0&&ww>0)
{
int mi=min(w,ww);
w-=mi,ww-=mi;
numa[ta[x1][1]]-=mi;
numb[tb[x2][1]]-=mi;
dfs(ta[x1][1],tb[x2][1],len-1,res,mi);
}
if(z>0&&ww>0)
{
int mi=min(z,ww);
z-=mi,ww-=mi;
dfs(ta[x1][0],tb[x2][1],len-1,res^(1<<len),mi);
numa[ta[x1][0]]-=mi;
numb[tb[x2][1]]-=mi;
}
if(w>0&&zz>0)
{
int mi=min(w,zz);
w-=mi,zz-=mi;
numa[ta[x1][1]]-=mi;
numb[tb[x2][0]]-=mi;
dfs(ta[x1][1],tb[x2][0],len-1,res^(1<<len),mi);
}
return ;
}
int main(){
int t;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
add(a[i],ta,numa,tota);
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&b[i]);
add(b[i],tb,numb,totb);
}
dfs(0,0,29,0,n);
sort(ans,ans+cnt);
for(int i=0;i<cnt;i++)
{
printf("%d%c",ans[i],i==cnt-1?'\n':' ');
}
}
}
1004 equation
题意
给定
a
i
,
b
i
a_i,b_i
ai,bi和C,求解方程
∑
i
=
1
N
∣
a
i
⋅
x
+
b
i
∣
=
C
∑_{i=1}^N|a_i⋅x+b_i|=C
∑i=1N∣ai⋅x+bi∣=C。
思路
因为每一项都有绝对值,这是无法化去的。但是题目中N仅有1e5,于是就可以暴力求解了。首先对于每个
∣
a
i
⋅
x
+
b
i
∣
|a_i⋅x+b_i|
∣ai⋅x+bi∣,当
x
≥
−
b
i
a
i
x\geq -\frac {b_i} {a_i}
x≥−aibi则绝对值内为正。那么我们对于每一项都关于
−
b
i
a
i
-\frac {b_i} {a_i}
−aibi进行排序,就能知道x取任何值时
a
i
⋅
x
+
b
i
a_i⋅x+b_i
ai⋅x+bi的正负性了。
原式化为
(
a
1
+
a
2
+
a
3
+
.
.
.
.
+
a
n
)
x
+
(
b
1
+
b
2
+
b
3
+
.
.
.
.
.
+
b
n
)
=
C
(a_1+a_2+a_3+....+a_n)x+(b_1+b_2+b_3+.....+b_n)=C
(a1+a2+a3+....+an)x+(b1+b2+b3+.....+bn)=C
其中
s
u
m
(
a
)
sum (a)
sum(a)和
s
u
m
(
b
)
sum (b)
sum(b)可以用前缀和优化一下,然后求出x看是否满足要求即可
当
s
u
m
(
a
)
=
0
sum (a)=0
sum(a)=0,且
s
u
m
(
b
)
=
C
sum (b)=C
sum(b)=C则为无穷解(与x无关)
当
s
u
m
(
a
)
=
0
sum (a)=0
sum(a)=0,且
s
u
m
(
b
)
=
̸
C
sum (b)=\not C
sum(b)≠C则为无解
有几个坑点,后面比较大小的时候因为是用乘法比较,所以必须确保a是正数
还有排序的时候cmp千万不能写成大于等于,因为这个我T到哭出来
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#define lson node<<1
#define rson node<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=998244353;
const ll inf=1e15;
struct node{
ll a;
ll b;
/*bool operator < (node const &x)const{
return b*x.a >= a*x.b;
}*/
}f[maxn];
ll qa[maxn];
ll qb[maxn];
node ans[maxn];
bool cmp(node x1,node x2)
{
return x1.b*x2.a>x2.b*x1.a;
}
bool cmp2(node x1,node x2)
{
return x1.b*x2.a>=-x2.b*x1.a;
}
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(){
int t;
int n;
ll c;
scanf("%d",&t);
//vector<node> ans;
while(t--)
{
//ans.clear();
scanf("%d%lld",&n,&c);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&f[i].a,&f[i].b);
}
sort(f+1,f+1+n,cmp);
qa[0]=qb[0]=0;
for(int i=1;i<=n;i++)
{
qa[i]=qa[i-1]+f[i].a;
qb[i]=qb[i-1]+f[i].b;
}
int cnt=0;
f[n+1].b=-inf;
f[n+1].a=1;
f[0].b=inf;
f[0].a=1;
int flag=0;
for(int i=0;i<=n;i++)
{
if(f[i].a*f[i+1].b==f[i].b*f[i+1].a)
{
continue;
}
ll ansa=-qa[n];
ll ansb=-qb[n];
ansa+=2*qa[i];
ansb+=2*qb[i];
if(ansa==0)
{
if(ansb==c)
{
printf("-1\n");
flag=1;
break;
}
else{
continue;
}
}
node temp;
temp.a=ansa;
temp.b=(c-ansb);
if(temp.a<0)
{
temp.b=-temp.b;
temp.a=-temp.a;
}
//if(i==3)
//cout<<temp.b*f[i].a<<">="<<-f[i].b*temp.a<<endl;
//cout<<temp.b*f[i+1].a<<"<"<<-f[i+1].b*temp.a<<endl;
if(cmp2(temp,f[i])&&(!cmp2(temp,f[i+1])))
{
ll emm=gcd(abs(ansa),abs(c-ansb));
temp.a/=emm;
temp.b/=emm;
//ans.push_back(temp);
ans[++cnt]=temp;
}
}
if(!flag)
{
//int len=ans.size();
printf("%d",cnt);
for(int i=1;i<=cnt;i++)
{
printf(" %lld/%lld",ans[i].b,ans[i].a);
}
printf("\n");
}
}
}
1007 permutation 2
题意
给定n,x,y,求1-n的数满足
①头尾是x,y
②相邻两个数差值小于等于2
的排列有多少种
思路
队友nb的一匹,直接打表找完规律过了,剩下我一脸懵比。
赛后开始想正解,首先将排列转化为在一条链上跳跃,每次可以-2,-1,+1,+2四种方式跳跃,需要满足从x跳到y,且必须经过每一个点,不能重复走。这样这个问题就更形象了。
对于x来说如果x前有数,那么他下一步必须往回跳,因为一旦往后跳了就无法再跳x点前的数了,并且下一步往回跳也仅有一种跳法能跳过所有的数最终到达x+1。这个其实画张图就能想明白,同理对于y也一样。也就意味着,若x前有数就把起点看作是x+1,之前的部分都不用管。
那么问题就再度简化为起点前和终点后都没有数的情况,这很显然是个dp。
令
d
p
[
i
]
dp[i]
dp[i]表示长度为i的情况,则转移方程为
d
p
[
i
]
=
d
p
[
i
−
1
]
+
d
p
[
i
−
3
]
dp[i]=dp[i-1]+dp[i-3]
dp[i]=dp[i−1]+dp[i−3]
d
p
[
i
−
1
]
dp[i-1]
dp[i−1]很好理解只需要跳一步就能转移,但为什么有
d
p
[
i
−
3
]
dp[i-3]
dp[i−3]呢,为什么不是
d
p
[
i
−
2
]
dp[i-2]
dp[i−2]或者其他东西呢?
首先
d
p
[
i
−
2
]
dp[i-2]
dp[i−2]是一定不能转移过来的,因为要保证每个点都走过,而i-1还没走,所以不能直接转移,需要通过i-1转移才行。
对于
d
p
[
i
−
3
]
dp[i-3]
dp[i−3]有这种情况
(
i
−
3
)
−
>
(
i
−
1
)
−
>
(
i
−
2
)
−
>
(
i
−
4
)
(i-3)->(i-1)->(i-2)->(i-4)
(i−3)−>(i−1)−>(i−2)−>(i−4)
这种情况是在之前的dp中都没有的,因此不会重复。对于什么(i-4)(i-5)啦本质上都是这种情况的变形,如果也转移就重复了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define lson node<<1
#define rson node<<1|1
using namespace std;
typedef long long ll;
const ll inf=(ll)(2e5)*(ll)(1e9)+900;
const ll mod=998244353;
const int maxn = 1e5+20;
int n,x,y;
ll dp[maxn];
void init()
{
dp[0]=0;
dp[1]=dp[2]=dp[3]=1;
for(int i=4;i<=maxn-2;i++)
{
dp[i]=(dp[i-1]+dp[i-3])%mod;
}
}
int main(){
int t;
init();
cin>>t;
while(t--)
{
scanf("%d%d%d",&n,&x,&y);
int len=y-x+1;
if(x>1)
len--;
if(y<n)
len--;
if(len<=0){
printf("0\n");
continue;
}
printf("%lld\n",dp[len]);
}
}