对于上周的总结

上周的训练断了,可能也为 ICPC\text{ICPC} 爆炸埋下种子。

毕竟这东西一两周的积累远远不够

这周末打完西安赛后,应该会空出很多时间,准备开始机器学习的学习以及部分生信的学习。

11月7日

写了下 CF 1750 的 C D

CF1750 C. Complementary XOR

题目大意,给你 a,ba,b 两个 01 串,询问是否能通过以下操作 f(l,r)f(l,r)a,ba,b 变成两个全为 0 的序列

f(l,r)f(l,r) 操作把 i[l,r]i\in [l,r] 中的 aia_i 变为 iaii-a_i ,将i[l,r]i\notin[l,r]bib_i 变为 1bi1-b_i

首先我们考虑 a=b,a=¬ba=b,a=\neg b 这两个都有一个很显然的结论,如果 ai=bi=1a_i=b_i=1 那么我们可以进行 f(1,r),f(1,r1)f(1,r),f(1,r-1) 之后ai=bi=0a_i=b_i=0

我们前面的都相等,到 nn 不满足,此时我们无论是改 an,bna_n,b_n 都不可以,这种情况不可能成立。

实际上题目给了保证能在 n+5n+5 步得到答案,所以这里更可能是个小范围打表找规律,然后做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>

using namespace std;

const int maxn=100100;

int n;
char a[maxn],b[maxn];
int T,cnt;

struct sol{
int l,r;
}q[maxn];

void solve()
{
int lk1=0,lk2=0; cnt=0;
for(int i=1;i<=n;i++)
{
lk1+=(a[i]!=b[i]);
lk2+=a[i]==b[i];
}
if(lk1==n)
{
q[++cnt].l=1;
q[cnt].r=n;
for(int i=1;i<=n;i++) a[i]=b[i];
}
if(lk1==n||lk2==n)
{
cout<<"Yes\n";
}
else
{
cout<<"No\n";
return ;
}
if(a[1]=='1')
{
q[++cnt].l=1;
q[cnt].r=n;
q[++cnt].l=2;
q[cnt].r=n;
}
int las=1;
for(int i=2;i<=n;i++)
{
if(a[i]=='0')
{
las=i;
continue;
}
while(a[i]==a[i+1]&&i+1<=n) i++;
q[++cnt].l=1;
q[cnt].r=i;
q[++cnt].l=1;
q[cnt].r=las;
}
cout<<cnt<<endl;
for(int i=1;i<=cnt;i++)
{
cout<<q[i].l<<" "<<q[i].r<<endl;
}
}

int main()
{
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
solve();
}
return 0;
}

CF1750 D. Count GCD

题目大意,给你一个序列,满足 gcd(b1,b2,...,bi)=ai\gcd(b_1,b_2,...,b_i)=a_i ,并且 bi<=mb_i<=m 询问有多少个 bnb_n 满足。

首先第一个数是确定的,为a1a_1,考虑第二个位置的数字

如果 a1<a2a_1<a_2 说明无解

如果 a1=a2a_1=a_2 说明 b2=ma1b_2=\lfloor\frac{m}{a_1}\rfloor

如果 a1>a2a_1>a_2

如果 a2a1a_2 | a_1 我们要求的即为 (1,m)(1,m)gcd(a1,x)=a2\gcd(a_1,x)=a_2xx 个数,这就是一个很显然的容斥。

其他情况下,解不存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>

using namespace std;

const int maxn=200200;
const int modp=998244353;

int n,m,a[maxn];
int T;
int p[maxn*10],cnt,inp[maxn*10];

void getprime(int n)
{
inp[1]=1;
for(int i=2;i<=n;i++)
{
if(!inp[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;j++)
{
inp[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
}

void solve()
{
int ans=1;
int tmp=a[1];
for(int i=2;i<=n;i++)
{
if(a[i]==tmp) ans=(1ll*ans*(m/tmp)%modp);
if(a[i]>tmp)
{
ans=0;
break;
}
if(a[i]<tmp)
{
int mmp=0;
if(tmp%a[i]==0)
{
// mmp=a[i];
mmp=tmp/a[i];
tmp=a[i];
int sz=0,d[30]={0};
for(int j=1;p[j]*p[j]<=mmp;j++)
{
if(mmp%p[j]==0)
{
d[++sz]=p[j],mmp/=p[j];
while(mmp%p[j]==0) mmp/=p[j];
}
}
if(mmp>1) d[++sz]=mmp;
int aans=0;
for(int s=1;s<=(1<<sz)-1;s++)
{
int lk=1;
int cc=0;
for(int j=1;j<=sz;j++)
{
if(1<<(j-1)&s) lk*=d[j],cc++;
}
aans+=((m/a[i])-(m/a[i])/lk)*((cc&1)?1:-1);
}
ans=(1ll*ans*aans)%modp;
}
else
{
cout<<"0\n";
return ;
}
}
}
cout<<ans<<endl;
}

int main()
{
ios::sync_with_stdio(false);
cin>>T;
getprime(1000000);
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
solve();
}
}

11月8日

今天过了俩板子,一个st表一个线段树2

CF1747D. Yet Another Problem

题目大意,给你一个 01 序列,询问 (l,r)(l,r) 区间最少操作多少次可以把这个区间变为全 0 ,不能的话输出 -1 ,每次询问互不影响。注意,每次只能操作长度为奇数的区间

我们先考虑无解的情况:

这个区间所有数字异或和不为 00

这个区间形如 2 2 4 4 此时只能对偶数长度区间操作,也不可以

考虑答案为0 的情况

这个区间全为 00

考虑答案为1

区间长度为奇数
长度为偶数的区间有一个端点是 00

考虑答案为 2

长度为偶数的区间分成两个长度为奇数的区间然后进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<bits/stdc++.h>

#define endl '\n'
using namespace std;

const int maxn=200100;

int n,m;
int a[maxn],s[maxn],b[maxn],cnt;
int sx[maxn];
int v1[maxn],v2[maxn],f1[maxn],f2[maxn];

int main()
{
// freopen("1.txt","r",stdin);
// freopen("-1.txt","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
int las=0;
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]^a[i];
sx[i]=sx[i-1]+(!a[i]);
b[++cnt]=s[i];
}
b[++cnt]=0;
sort(b+1,b+cnt+1);
cnt=unique(b+1,b+cnt+1)-b-1;
for(int i=0;i<=n;i++)
{
s[i]=lower_bound(b+1,b+cnt+1,s[i])-b;
}
memset(f1,0x3f,sizeof(f1));
memset(f2,0x3f,sizeof(f2));
for(int i=n;i>=0;i--)
{
if(i&1)
{
if(v1[s[i]]) f1[i]=v1[s[i]];
if(v2[s[i]]) f2[i]=v2[s[i]];
v1[s[i]]=i;
}
else
{
if(v1[s[i]]) f1[i]=v1[s[i]];
if(v2[s[i]]) f2[i]=v2[s[i]];
v2[s[i]]=i;
}
}
for(int i=1;i<=m;i++)
{
int l,r;
cin>>l>>r;
if((r-l+1)&1)
{
if(s[r]^s[l-1]) cout<<-1<<endl;
else if(sx[r]-sx[l-1]==r-l+1) cout<<0<<endl;
else cout<<1<<endl;
}
else
{
if(s[r]^s[l-1]) cout<<-1<<endl;
else
{
if(r-l+1==2)
{
if(a[l]==a[r]&&a[l]==0) cout<<0<<endl;
else if(a[l]==a[r]) cout<<-1<<endl;
else cout<<-1<<endl;
}
else if(sx[r]-sx[l-1]==r-l+1) cout<<0<<endl;
else if(((l-1)%2==1&&f2[l-1]>=r)||((l-1)%2==0&&f1[l-1]>=r)) cout<<-1<<endl;
else
{
if(!a[l]||!a[r]) cout<<1<<endl;
else cout<<2<<endl;
}
}
}
}
return 0;
}

还学了下kruskal 重构树,写道另一篇博客里面了

11月9日

今天主要是在偷懒,过了威海的I

I. Dragon Bloodline

题目大意,生产某种物品有 nn 种资源需求,每种资源需求 aia_i ,同时你可以以 2i12^{i-1} 速率生产,并且最多有 bib_i 个,询问最多能生产多少个。

很显然的,最多能生产多少可以用一个二分,于是此题难点在于怎么把数字分成 nn 组,满足每组能生长 ai\geq a_i

考虑到取等的时候,此题变为 np\text{np} 所以肯定不是分组背包。

那么我们可以贪心让大的数字尽可能发挥作用,令现在我们二分的答案是 kk ,当2i1aik2^{i-1} \leq a_i*k 说明这个数字完全发挥作用,可以放进去。

按照这个道理放数字就可以了,还需要注意二分的边界判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include<bits/stdc++.h>

#define int long long

using namespace std;

const int maxn=50050;

int n,m,a[maxn],b[maxn],T;
int c[maxn];

bool cmp(const int x,const int y)
{
return x>y;
}

int fd(int x)
{
int l=1,r=n,ans=0,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(c[mid]>=x) l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}

int check(int x)
{
for(int i=1;i<=n;i++) c[i]=a[i]*x;
for(int i=m;i;i--)
{
sort(c+1,c+n+1,cmp);
int tmp=b[i];
int lk=1<<(i-1);
int k=fd(lk);
if(tmp<=k)
{
for(int j=1;j<=tmp;j++) c[j]-=lk;
continue;
}
// for(int j=1;j<=k;j++) c[j]-=lk;
// tmp-=k;
// sort(c+1,c+n+1,cmp);
for(int j=1;j<=n&&tmp>=1;j++)
{
if(c[j]<=0)
{
// tmp=0;
break;
}
else if(tmp<c[j]/lk)
{
c[j]-=lk*tmp;
tmp=0;
}
else tmp-=c[j]/lk,c[j]%=lk;
}
sort(c+1,c+n+1,cmp);
for(int j=1;j<=min(tmp,n);j++) c[j]-=lk;
}
sort(c+1,c+n+1,cmp);
if(c[1]<=0) return 1;
return 0;
}

int solve()
{
cin>>n>>m;
int mx=0;
for(int i=1;i<=n;i++) cin>>a[i],mx=max(mx,a[i]);
for(int i=1;i<=m;i++) cin>>b[i];
int l=1,r=2e15/mx,mid,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
cout<<ans<<endl;
}

signed main()
{
// freopen("1.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T--)
{
solve();
}
return 0;
}

十一月十日

过了威海的 G\text{G}

题目大意,给定 l,rl,r,求 k=lr[gcd(kxx,x)=1]\sum_{k=l}^r[\text{gcd}(kx \oplus x,x)=1]

我们有个猜想,假设 2k2^k 是第一个比 xx 大的数字,那么就有一个 2k2^k 的循环节。

然后打表就把这题过了(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<bits/stdc++.h>

#define int long long

#define endl '\n'

using namespace std;

const int maxn=2002000;

int s[maxn],x,n;

int gcd(int x,int y)
{
if(!y) return x;
return gcd(y,x%y);
}

signed main()
{
ios::sync_with_stdio(false);
cin>>x>>n;
int lk=1;
while(lk<x) lk<<=1;
for(int i=1;i<=lk;i++)
{
s[i]=s[i-1]+(gcd(x*i^x,x)==1);
}
int l,r,rl,rr;
for(int i=1;i<=n;i++)
{
cin>>l>>r;
int ans=0;
if(l%lk==0&&r%lk==0) cout<<s[lk]*((r-l+1)/lk)<<endl;
else if(l%lk==0)
{
ans+=s[lk]*((r-l)/lk);
ans+=s[r%lk];
cout<<ans<<endl;
}
else if(r%lk==0)
{
ans+=s[lk]*((r-l)/lk);
ans+=s[lk]-s[l%lk-1];
cout<<ans<<endl;
}
else
{
rl=(l-1)/lk+1; rl*=lk;
rr=(r)/lk; rr*=lk;
if(rr-rl>=0)
{
ans+=(rr-rl)/lk*s[lk];
ans+=s[lk]-s[l%lk-1];
ans+=s[r%lk];
cout<<ans<<endl;
}
else
{
ans+=s[r%lk]-s[l%lk-1];
cout<<ans<<endl;
}
}

}
}