题意
t
t
t组样例,给定一个长度为
n
n
n的数组,可以对这个数组进行任意次操作,每次操作时选取一个区间
[
l
,
r
]
[l,r]
[l,r],对于所有
i
∈
[
0
,
r
?
l
]
i\in[0,r-l]
i∈[0,r?l],将这个区间内的
a
l
+
i
a_{l+i}
al+i?变为
a
l
+
i
&
a
r
?
i
a_{l+i} \& a_{r-i}
al+i?&ar?i?,最小化这个数组中的最大值,输出这个最大值。
1
≤
t
≤
100
,
1
≤
n
≤
100
,
0
≤
a
i
≤
1
0
9
1\leq t \leq100,1\leq n \leq100,0\leq a_i \leq10^9
1≤t≤100,1≤n≤100,0≤ai?≤109
Solution
将
a
i
a_i
ai?拆分为二进制,如果当前位对于所有的
a
i
a_i
ai?来说都为1,则说明,无论如何选取区间,这一位上都不可能变为0,否则经过若干次变化后,这一位上最终都会变为0。
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ls i<<1
#define rs i<<1|1
typedef long long ll;
typedef pair<int,int>P;
int t,n;
int a[105];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
int cnt=0;
while(x)
{
if(x%2)a[cnt]++;
cnt++;
x/=2;
}
}
int res=0;
for(int i=0;i<=32;i++)
{
if(a[i]==n)res+=(1<<i);
}
printf("%d\n",res);
}
return 0;
}
题意
t
t
t组样例,给定一个长度为
n
n
n的字符串,包含字符‘?’,‘R’,‘B’,其中’?'代表黑色正方形,‘R’代表红色正方形,‘B’代表蓝色正方形,现在要求给黑色正方形染色,使得不完美性(相邻正方形为同一颜色的的对数)最少。 输出任意构造方案。
1
≤
t
≤
100
,
1
≤
n
≤
100
1\leq t\leq100,1\leq n\leq 100
1≤t≤100,1≤n≤100
Solution
贪心,(bfs) 对一个黑色正方形涂色,对不完美性的贡献可能为0或1或2。 因此对一个不为黑色正方形的正方形旁边为黑色的正方形涂色是,只要与当前颜色不同即可。这样构造出来的方案是最优的。
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ls i<<1
#define rs i<<1|1
typedef long long ll;
typedef pair<int,int>P;
int t,n;
char s[105];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%s",&n,s);
queue<int>q;while(!q.empty())q.pop();
for(int i=0;i<n;i++)if(s[i]!='?')q.push(i);
if(!q.empty())
{
while(!q.empty())
{
int x=q.front();q.pop();
if(x-1>=0&&s[x-1]=='?')
{
s[x-1]=(s[x]=='R'?'B':'R');
q.push(x-1);
}
if(x+1<n&&s[x+1]=='?')
{
s[x+1]=(s[x]=='R'?'B':'R');
q.push(x+1);
}
}
}
else
{
for(int i=0;i<n;i++)
{
if(i%2==0)s[i]='R';
else s[i]='B';
}
}
printf("%s\n",s);
}
return 0;
}
题意
t
t
t组样例,给了一个数字
n
n
n,表示有
n
+
1
n+1
n+1个点。 一个长度为
n
n
n的数组
a
a
a: 1.
a
i
a_i
ai?为0,代表
i
i
i到
n
+
1
n+1
n+1有一条边。 2.
a
i
a_i
ai?为1,代表
n
+
1
n+1
n+1到
i
i
i有一条边。 还有
n
?
1
n-1
n?1条边,从
i
i
i到
i
+
1
i+1
i+1,
i
∈
[
1
,
n
?
1
]
i\in[1,n-1]
i∈[1,n?1] 问是否有存在一条路径,在这条路径上,每个点都只经过一次。 若存在,输出这条路径;不存在,输出-1.
1
≤
t
≤
20
,
1
≤
n
≤
1
0
4
,
0
≤
a
i
≤
1
1\leq t\leq 20,1\leq n\leq10^4,0\leq a_i\leq1
1≤t≤20,1≤n≤104,0≤ai?≤1
Solution
1.如果
a
1
=
1
a_1=1
a1?=1,可以构造出
n
+
1
,
1
,
2
,
3
,
?
?
,
n
?
1
,
n
n+1,1,2,3,\cdots,n-1,n
n+1,1,2,3,?,n?1,n这样的路径。 2.如果
a
n
=
0
a_n=0
an?=0,可以构造出
1
,
2
,
3
,
?
?
,
n
?
1
,
n
,
n
+
1
1,2,3,\cdots,n-1,n,n+1
1,2,3,?,n?1,n,n+1 3.如果
a
i
?
1
=
0
&
&
a
i
=
1
,
?
i
∈
[
2
,
n
]
a_{i-1}=0\& \& a_{i}=1,\exist i\in[2,n]
ai?1?=0&&ai?=1,?i∈[2,n],可以构造出
1
,
2
,
?
?
,
i
,
n
+
1
,
i
+
1
,
i
+
2
,
?
?
,
n
?
1
,
n
1,2,\cdots,i,n+1,i+1,i+2,\cdots,n-1,n
1,2,?,i,n+1,i+1,i+2,?,n?1,n 4.否则输出-1(虽然这种情况好像不会出现,但还是写上了)
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ls i<<1
#define rs i<<1|1
typedef long long ll;
typedef pair<int,int>P;
int t;
int n;
int a[100005];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
bool flag=false;
if(a[n]==0)
{
for(int i=1;i<=n+1;i++)printf("%d ",i);
printf("\n");
continue;
}
else if(a[1]==1)
{
printf("%d ",n+1);
for(int i=1;i<=n;i++)printf("%d ",i);
printf("\n");
continue;
}
for(int i=2;i<=n;i++)
{
if(a[i-1]==0&&a[i]!=0)flag=true;
}
if(!flag)printf("-1\n");
else
{
printf("1 ");
for(int i=2;i<=n;i++)
{
if(a[i-1]==0&&a[i]!=0)
{
printf("%d ",n+1);
for(int j=i;j<=n;j++)
printf("%d ",j);
printf("\n");
break;
}
else printf("%d ",i);
}
}
}
return 0;
}
题意
给定节点个数
n
n
n,边数分别为
m
1
,
m
2
m_1,m_2
m1?,m2?的两处森林(无环),问还能往两个森林中加入多少条边,输出加入边的方案。 往两个森林中加边的条件是,加入这条边后,两个森林都不会产生环。
Solution
先将两个森林中的边通过并查集分别并起来,然后暴力枚举加入哪条边,如果能加入这条边,则把其放入答案,两个森林分别和这条边并起来。
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ls i<<1
#define rs i<<1|1
typedef long long ll;
typedef pair<int,int>P;
int n,m1,m2;
int f1[1005];
int f2[1005];
int Find1(int x){return f1[x]==x?x:f1[x]=Find1(f1[x]);}
int Find2(int x){return f2[x]==x?x:f2[x]=Find2(f2[x]);}
vector<P>e;
int main()
{
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<=n;i++)f1[i]=f2[i]=i;
for(int i=1;i<=m1;i++)
{
int u,v;scanf("%d%d",&u,&v);
u=Find1(u);
v=Find1(v);
f1[u]=v;
}
for(int i=1;i<=m2;i++)
{
int u,v;scanf("%d%d",&u,&v);
u=Find2(u);
v=Find2(v);
f2[u]=v;
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
int u1=Find1(i);
int v1=Find1(j);
int u2=Find2(i);
int v2=Find2(j);
if(u1!=v1&&u2!=v2)
{
f1[u1]=v1;
f2[u2]=v2;
e.push_back(P(i,j));
}
}
}
printf("%d\n",e.size());
for(P p:e)printf("%d %d\n",p.first,p.second);
return 0;
}
题意
给定节点个数
n
n
n,边数分别为
m
1
,
m
2
m_1,m_2
m1?,m2?的两处森林(无环),问还能往两个森林中加入多少条边,输出加入边的方案。 往两个森林中加边的条件是,加入这条边后,两个森林都不会产生环。
Solution
正解是启发式合并。
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ls i<<1
#define rs i<<1|1
typedef long long ll;
typedef pair<int,int>P;
int n,m1,m2;
int f1[100005];
int f2[100005];
int Find1(int x){return f1[x]==x?x:f1[x]=Find1(f1[x]);}
int Find2(int x){return f2[x]==x?x:f2[x]=Find2(f2[x]);}
vector<P>e;
int main()
{
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<=n;i++)f1[i]=f2[i]=i;
for(int i=1;i<=m1;i++)
{
int u,v;scanf("%d%d",&u,&v);
u=Find1(u);
v=Find1(v);
if(u<v)swap(u,v);
f1[u]=v;
}
for(int i=1;i<=m2;i++)
{
int u,v;scanf("%d%d",&u,&v);
u=Find2(u);
v=Find2(v);
if(u<v)swap(u,v);
f2[u]=v;
}
for(int i=2;i<=n;i++)
{
int u=Find1(i);
int v=Find2(i);
if(u!=Find1(1)&&v!=Find2(1))
{
f1[u]=1;
f2[v]=1;
e.push_back(P(1,i));
}
}
set<int>st1,st2;
for(int i=2;i<=n;i++)
{
if(Find1(i)!=Find1(1))st1.insert(Find1(i));
if(Find2(i)!=Find2(1))st2.insert(Find2(i));
}
for(auto u=st1.begin(),v=st2.begin();u!=st1.end()&&v!=st2.end();u++,v++)
{
e.push_back(P(*u,*v));
}
printf("%d\n",e.size());
for(P p:e)printf("%d %d\n",p.first,p.second);
return 0;
}
题意
问有多少个数组a满足以下条件:
对
于
所
有
的
i
(
1
≤
i
≤
n
)
,
a
i
是
[
l
i
,
r
i
]
之
间
的
整
数
∑
i
=
1
n
a
i
≤
m
g
c
d
(
a
1
,
a
2
,
?
?
,
a
n
)
=
1
对于所有的i(1\leq i \leq n),a_i是[l_i,r_i]之间的整数 \\ \sum_{i=1}^na_i\leq m \\ gcd(a_1,a_2,\cdots,a_n)=1
对于所有的i(1≤i≤n),ai?是[li?,ri?]之间的整数i=1∑n?ai?≤mgcd(a1?,a2?,?,an?)=1
m
,
n
m,n
m,n题目中已经给出。
1
≤
n
≤
50
,
1
≤
m
≤
1
0
5
1\leq n \leq 50,1\leq m\leq 10^5
1≤n≤50,1≤m≤105
Solution
对于
g
c
d
(
a
1
,
a
2
,
?
?
,
a
n
)
=
1
gcd(a_1,a_2,\cdots,a_n)=1
gcd(a1?,a2?,?,an?)=1
∑
a
1
=
l
1
r
1
∑
a
2
=
l
2
r
2
?
∑
a
n
=
l
n
r
n
[
g
c
d
(
a
1
,
a
2
,
?
?
,
a
n
)
=
1
]
=
∑
a
1
=
l
1
r
1
∑
a
2
=
l
2
r
2
?
∑
a
n
=
l
n
r
n
∑
d
∣
g
c
d
(
a
1
,
a
2
,
?
?
,
a
n
)
μ
(
d
)
=
∑
d
=
1
m
μ
(
d
)
∑
a
1
=
?
l
1
d
?
?
r
1
d
?
∑
a
2
=
?
l
2
d
?
?
r
2
d
?
?
∑
a
n
=
?
l
n
d
?
?
r
n
d
?
1
\sum_{a_1=l_1}^{r_1}\sum_{a_2=l_2}^{r_2}\cdots \sum_{a_n=l_n}^{r_n}[gcd(a_1,a_2,\cdots,a_n)=1] \\ =\sum_{a_1=l_1}^{r_1}\sum_{a_2=l_2}^{r_2}\cdots \sum_{a_n=l_n}^{r_n}\sum_{d|gcd(a_1,a_2,\cdots,a_n)}\mu(d) \\ =\sum_{d=1}^{m}\mu(d)\sum_{a_1=\lceil \frac{l_1}{d}\rceil}^{\lfloor \frac{r_1}{d}\rfloor}\sum_{a_2=\lceil \frac{l_2}{d}\rceil}^{\lfloor \frac{r_2}{d}\rfloor}\cdots \sum_{a_n=\lceil \frac{l_n}{d}\rceil}^{\lfloor \frac{r_n}{d}\rfloor}1
a1?=l1?∑r1??a2?=l2?∑r2???an?=ln?∑rn??[gcd(a1?,a2?,?,an?)=1]=a1?=l1?∑r1??a2?=l2?∑r2???an?=ln?∑rn??d∣gcd(a1?,a2?,?,an?)∑?μ(d)=d=1∑m?μ(d)a1?=?dl1???∑?dr1????a2?=?dl2???∑?dr2?????an?=?dln???∑?drn????1 对于
∑
i
=
1
n
a
i
≤
m
\sum_{i=1}^na_i\leq m
∑i=1n?ai?≤m 可以通过背包dp来优化+前缀和
n
o
w
∈
[
1
,
n
]
,
表
示
表
示
当
前
处
理
到
a
n
o
w
记
L
为
a
n
o
w
的
下
边
界
,
R
为
a
n
o
w
的
上
边
界
s
u
m
[
j
]
表
示
∑
i
=
1
n
o
w
a
i
≤
j
的
个
数
f
[
j
]
表
示
∑
i
=
1
n
o
w
a
i
=
j
的
个
数
因
此
s
u
m
[
j
]
=
f
[
j
]
+
(
i
?
s
u
m
[
j
?
1
]
:
0
)
来
维
护
前
缀
和
f
[
j
]
=
(
j
>
=
L
?
s
u
m
[
j
?
L
]
:
0
)
?
(
j
>
=
R
+
1
?
s
u
m
[
j
?
R
?
1
]
:
0
)
这
个
表
示
有
多
少
种
和
为
j
的
方
案
那
么
为
什
么
f
[
j
]
是
这
样
推
过
来
的
?
?
?
拆
成
两
个
数
更
好
理
解
一
点
(
j
?
R
)
+
R
,
(
j
?
R
+
1
)
+
R
?
1
,
(
j
?
R
+
2
)
+
R
?
2
,
?
?
,
(
j
?
L
)
+
L
也
就
是
相
当
于
求
这
段
区
间
f
[
j
?
R
]
,
f
[
j
?
R
+
1
]
,
?
?
,
f
[
j
?
L
]
的
和
。
这
段
之
前
已
经
记
录
过
前
缀
和
了
,
也
就
是
(
j
>
=
L
?
s
u
m
[
j
?
L
]
:
0
)
?
(
j
>
=
R
+
1
?
s
u
m
[
j
?
R
?
1
]
:
0
)
now \in[1,n],表示表示当前处理到a_{now} \\ 记L为a_{now}的下边界,R为a_{now}的上边界 \\ sum[j]表示\sum_{i=1}^{now}a_i\leq j的个数 \\ f[j]表示\sum_{i=1}^{now}a_i=j的个数 \\ 因此 sum[j]=f[j]+(i?sum[j-1]:0)来维护前缀和 \\ f[j]=(j>=L?sum[j-L]:0)-(j>=R+1?sum[j-R-1]:0)这个表示有多少种和为j的方案 \\ 那么为什么f[j]是这样推过来的???拆成两个数更好理解一点 \\ (j-R)+R,(j-R+1)+R-1,(j-R+2)+R-2,\cdots,(j-L)+L \\ 也就是相当于求这段区间f[j-R],f[j-R+1 ],\cdots,f[j-L]的和。 \\ 这段之前已经记录过前缀和了,也就是(j>=L?sum[j-L]:0)-(j>=R+1?sum[j-R-1]:0)
now∈[1,n],表示表示当前处理到anow?记L为anow?的下边界,R为anow?的上边界sum[j]表示i=1∑now?ai?≤j的个数f[j]表示i=1∑now?ai?=j的个数因此sum[j]=f[j]+(i?sum[j?1]:0)来维护前缀和f[j]=(j>=L?sum[j?L]:0)?(j>=R+1?sum[j?R?1]:0)这个表示有多少种和为j的方案那么为什么f[j]是这样推过来的???拆成两个数更好理解一点(j?R)+R,(j?R+1)+R?1,(j?R+2)+R?2,?,(j?L)+L也就是相当于求这段区间f[j?R],f[j?R+1],?,f[j?L]的和。这段之前已经记录过前缀和了,也就是(j>=L?sum[j?L]:0)?(j>=R+1?sum[j?R?1]:0)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int N=1e5+5;
const int mod=998244353;
int n,m;
int l[N],r[N];
int prime[N],cnt=0;
int mu[N];
bool isprime[N];
int f[N];
int sum[N];
void initMu()
{
mu[1]=1;
for(int i=2;i<=m;i++)
{
if(!isprime[i])
{
mu[i]=-1;
prime[cnt++]=i;
}
for(int j=0;j<cnt&&1ll*i*prime[j]<=m;j++)
{
isprime[i*prime[j]]=true;
if(i%prime[j])mu[i*prime[j]]=-mu[i];
else break;
}
}
}
int calc(int d)
{
int k=m/d;
f[0]=1;
for(int i=1;i<=k;i++)f[i]=0;
for(int i=1;i<=n;i++)
{
int L=(l[i]+d-1)/d;
int R=r[i]/d;
if(L>R)return 0;
for(int j=0;j<=k;j++)
{
sum[j]=f[j];
if(j)sum[j]=(sum[j]+sum[j-1])%mod;
}
for(int j=0;j<=k;j++)
{
if(j-L>=0)f[j]=sum[j-L];
else f[j]=0;
if(j-R-1>=0)f[j]=(f[j]-sum[j-R-1]+mod)%mod;
}
}
int ans=0;
for(int i=1;i<=k;i++)ans=(ans+f[i])%mod;
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]);
initMu();
int res=0;
for(int i=1;i<=m;i++)res=(res+1ll*mu[i]*calc(i)%mod+mod)%mod;
printf("%d",res);
return 0;
}
|