本文同步发布于个人博客
这场打着打着就睡着了,所以基本都是赛后补题,感觉这场有点数学场
C. 算概率
题意简述
有
n
n
n道题目,每道题目有一个正确概率
p
i
p_i
pi,问这
n
n
n道题里恰好做出
0
,
1
,
2
,
⋯
,
n
0,1,2,\cdots ,n
0,1,2,⋯,n道的概率分别是多少,对
1
0
9
+
7
10^9+7
109+7取模
注意这里对于分数
a
b
\frac{a}{b}
ba取模的意思是找到一个
q
q
q使得
b
⋅
q
m
o
d
(
1
0
9
+
7
)
=
a
b\cdot q\mod(10^9+7)=a
b⋅qmod(109+7)=a
题解
这个其实算是比较裸的二维dp吧)
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个数做对
j
j
j个数的概率,可以以第
i
i
i个数是否作对为切口,可得状态转移方程
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
⋅
(
1
−
p
i
)
+
f
[
i
−
1
]
[
j
−
1
]
⋅
p
i
f[i][j]=f[i-1][j]\cdot(1-p_i)+f[i-1][j-1]\cdot p_i
f[i][j]=f[i−1][j]⋅(1−pi)+f[i−1][j−1]⋅pi
注意在模的意义下
(
1
−
p
i
)
(1-p_i)
(1−pi)还是保持原来的性质的,可以设
b
−
a
b
=
q
\frac{b-a}{b}=q
bb−a=q和
a
b
=
q
\frac{a}{b}=q
ba=q不难推导出
p
=
q
p=q
p=q,之后就可以dp了,稍微注意一下初始化就行
AC代码
const int maxn=2050;
const ll mod=1e9+7;
ll n,p[maxn],f[maxn][maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>p[i];
f[0][0]=1;
for(int i=1;i<=n;i++)
{
f[i][0]=f[i-1][0]*(1-p[i]+mod)%mod;
for(int j=1;j<=i;j++)
f[i][j]=(f[i-1][j-1]*p[i]+f[i-1][j]*(1-p[i]+mod))%mod;
}
for(int i=0;i<=n;i++) cout<<f[n][i]<<" ";
cout<<endl;
return 0;
}
D. 数三角
题意简述
给出 n n n个点 ( 1 ≤ n ≤ 500 ) (1\leq n\leq 500) (1≤n≤500),求挑出三个点组成钝角三角形有多少种情况
题解
一开始以为500不能 n 3 n^3 n3枚举,后来发现这个复杂度没问题啊)就注意一下怎么判断钝角三角形就好了,这场好像有点卡距离的精度,不过本来也应该尽量避免浮点运算,我们可以用向量来处理,两个向量点乘小于0是钝角,还要考虑一下三点共线的情况,叉乘为0三点贡献把这种情况去除即可
AC代码
const int maxn=550;
int n;
int x[maxn],y[maxn];
bool calc(int a,int b,int c)
{
int flag=1;
if((x[c]-x[a])*(x[c]-x[b])+(y[c]-y[a])*(y[c]-y[b])>=0) return false;
if((x[c]-x[a])*(y[c]-y[b])-(x[c]-x[b])*(y[c]-y[a])==0) return false;
return true;
}
int main()
{
cin>>n;
int ans=0;
for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
for(int k=j+1;k<=n;k++)
{
if(calc(i,j,k)||calc(i,k,j)||calc(j,k,i)) ans++;
}
}
}
cout<<ans<<endl;
return 0;
}
E. 做计数
题意简述
找出不同的三元组 ( i , j , k ) (i,j,k) (i,j,k)满足 i + j = k \sqrt{i}+\sqrt{j}=\sqrt{k} i+j=k且 i ⋅ j ≤ n i\cdot j\leq n i⋅j≤n 当 i , j , k i,j,k i,j,k任意有一个不同时就认为三元组不同 ( 1 ≤ n ≤ 4 ⋅ 1 0 7 ) (1\leq n\leq4\cdot 10^7) (1≤n≤4⋅107)
题解
果然是好久没写高中数学了,这种技巧都忘记了
乍一看难以处理,我们其实可以两边平方变形成
i
+
j
+
i
⋅
j
=
k
i+j+\sqrt{i\cdot j}=k
i+j+i⋅j=k,这样
i
⋅
j
i\cdot j
i⋅j一定要是完全平方数,我们可以枚举小于等于
n
n
n的完全平方数,复杂度
O
(
n
)
O(\sqrt{n})
O(n)然后求这些数的约数对的个数即可,大概
O
(
n
)
O(n)
O(n)吧
AC代码
int n;
vector<int> a;
int main()
{
cin>>n;
int ans=0;
for(int i=1;i<=n/i;i++) a.push_back(i*i);
for(int i=0;i<a.size();i++)
{
int m=a[i];
for(int j=1;j<=m/j;j++)
{
if(m%j==0) ans+=2;
if(j*j==m) ans--;
}
}
cout<<ans<<endl;
return 0;
}
F. 拿物品
题意简述
有 n n n个数每个数两个属性 a i , b i a_i,b_i ai,bi,两个人 A A A与 B B B轮流拿,最后 A A A的分数是拿到的物品的 a a a属性之和, B B B则是 b b b属性之和,两人都希望最大化自己与对方的得分查,请你输出最后两人分别选择的集合
题解
有点博弈论的味道,以为两个人都绝对聪明,我们不妨考虑一下交换两个物品比如 ( a 1 , b 1 ) (a_1,b_1) (a1,b1)和 ( a 2 , b 2 ) (a_2,b_2) (a2,b2),那么 A ′ = A − a 1 + a 2 A'=A-a_1+a_2 A′=A−a1+a2, B ′ = B − b 1 + b 2 B'=B-b_1+b_2 B′=B−b1+b2,假如这时候变得更优的话,那么 A ′ − B ′ > A − B A'-B'>A-B A′−B′>A−B化简一下可得 a 1 + b 1 < a 2 + b 2 a_1+b_1<a_2+b_2 a1+b1<a2+b2而 B B B同理,所以他们都一定会取 a + b a+b a+b较大的数,那么直接排序然后输出即可
AC代码
const int maxn=200050;
int n;
struct node{
int id,a,b;
bool operator<(const node&t) const{
return (a+b)>(t.a+t.b);
}
}c[maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) c[i].id=i;
for(int i=1;i<=n;i++) read(c[i].a);
for(int i=1;i<=n;i++) read(c[i].b);
sort(c+1,c+n+1);
for(int i=1;i<=n;i++) if(i&1) cout<<c[i].id<<" ";
cout<<endl;
for(int i=1;i<=n;i++) if(!(i&1)) cout<<c[i].id<<" ";
return 0;
}
G. 判正误
题意简述
给出 a , b , c , d , e , f , g a,b,c,d,e,f,g a,b,c,d,e,f,g,判断 a d + b e + c f = g a^d+b^e+c^f=g ad+be+cf=g是否正确 ( − 1 0 9 ≤ a , b , c , g ≤ 1 0 9 , 0 ≤ d , e , f ≤ 1 0 9 ) (-10^9\leq a,b,c,g\leq 10^9,0\leq d,e,f\leq 10^9) (−109≤a,b,c,g≤109,0≤d,e,f≤109)
题解
这题显然无法直接写直接pow竟然可以过我们可以通过模相等来判断相等,为了防止被卡多取几个模数即可,不过赛场上写的代码太丑陋了,或许以后会改良一下吧)
AC代码
const ll p1=1222827239;
const ll p2=1e9+7;
const ll p3=2181271;
int T;
ll a,b,c,d,e,f,g;
ll p[]={1222827239,1000000007,2181271,122777,51787,9209};
ll ksm(ll a,ll b,int i)
{
ll res=1%p[i];
while(b)
{
if(b&1) res=(res*a)%p[i];
a=(a*a)%p[i];
b>>=1;
}
return res;
}
int main()
{
cin>>T;
while(T--)
{
cin>>a>>b>>c>>d>>e>>f>>g;
ll t1,t2,t3;
int flag=1;
for(int i=0;i<6;i++)
{
if(a<0)
{
t1=ksm(-a,d,i);
if(d%2==1) t1=(p[i]-t1)%p[i];
}
else if(a==0) t1=0;
else t1=ksm(a,d,i);
if(b<0)
{
t2=ksm(-b,e,i);
if(e%2==1) t2=(p[i]-t2)%p[i];
}
else if(b==0) t2=0;
else t2=ksm(b,e,i);
if(c<0)
{
t3=ksm(-c,f,i);
if(f%2==1) t3=(p[i]-t3)%p[i];
}
else if(c==0) t3=0;
else t3=ksm(c,f,i);
if(((t1+t2+t3)%p[i]+p[i])%p[i]==(g%p[i]+p[i])%p[i]) continue;
else flag=0;
}
if(flag==0) puts("No");
else puts("Yes");
}
return 0;
}
H. 施魔法
题意简述
有 n n n个元素,每次可以至少选择 k k k个元素消除,会消耗这些元素极差的能量,每个元素要恰好被消灭1次,问至少需要多少力量 ( 1 ≤ k ≤ n ≤ 3 ⋅ 1 0 5 , 0 ≤ a i ≤ 1 0 9 ) (1\leq k\leq n\leq 3\cdot 10^5,0\leq a_i \leq 10^9) (1≤k≤n≤3⋅105,0≤ai≤109)
题解
既然是极差的话我们首先很自然的想法就是排序,排完序以后的话因为必须至少取
k
k
k个数,假设当前取到
i
i
i的话,那么它可以从
i
−
k
+
1
i-k+1
i−k+1到
1
1
1的任意一个位置一直取到
i
i
i,当然前提是取了以后前面也要能够构成至少
k
k
k这样的性质,我们可以考虑一个dp,
f
[
i
]
f[i]
f[i]表示为前
i
i
i个所需要的最小能量,转移方程如下
f
[
i
]
=
m
i
n
(
f
[
i
]
,
f
[
j
−
1
]
+
a
[
i
]
−
a
[
j
]
)
,
(
1
≤
j
≤
i
−
k
+
1
)
f[i]=min(f[i],f[j-1]+a[i]-a[j]),(1\leq j\leq i-k+1)
f[i]=min(f[i],f[j−1]+a[i]−a[j]),(1≤j≤i−k+1)
也就是去枚举断点
j
j
j,注意一下由于前
k
−
1
k-1
k−1个
f
f
f显然不成立,因为哪怕全包含都小于
k
k
k,所以我们初始化时候全复制为inf这样就不会更新答案了,我们把
a
[
i
]
a[i]
a[i]提出来,也就是说我们只要去保存
f
[
j
−
1
]
−
a
[
j
]
f[j-1]-a[j]
f[j−1]−a[j]的最小值temp然后每次去更新即可,最后输出
f
[
n
]
f[n]
f[n]
AC代码
const int maxn=300050;
ll n,k,a[maxn],f[maxn];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++) read(a[i]);
sort(a+1,a+n+1);
for(int i=1;i<k;i++) f[i]=inf;
ll temp=-a[1];
for(int i=k;i<=n;i++)
{
f[i]=temp+a[i];
temp=min(temp,f[i-k+1]-a[i-k+2]);
}
cout<<f[n]<<endl;
return 0;
}
I. 建通道
题意简述
把 n n n个数相互建一些边要求所有数之间都存在一个路径可以到达,每条边所连的数 i , j i,j i,j产生能量 l o w b i t ( a [ i ] a [ j ] ) lowbit(a[i]^a[j]) lowbit(a[i]a[j]),求使得所消耗的能量最少的建图方式 ( 1 ≤ n ≤ 2 ⋅ 1 0 5 , 0 ≤ v i ≤ 2 3 0 ) (1\leq n\leq 2\cdot 10^5,0\leq v_i\leq 2^30) (1≤n≤2⋅105,0≤vi≤230)
题解
因为 l o w b i t lowbit lowbit的性质,所以我们肯定要对它造成贡献的1的位置越小越好,所以我们只要从小往大找数位,找到一个位既有0又有1即可,这样所有该位为0的往1连,所有该位为1的往0连,最后这两个一连,总共 n − 1 n-1 n−1条边,因为之前所有位所有的数都是相等的,所以异或为0,直到这一位产生了贡献,最后统计答案即可,注意有可能所有数或者一些数相等的情况,因为这些数连在一起异或起来肯定是0,所以可以先做去重即可
const int maxn=200050;
ll n,a[maxn];
ll vis[35][2],ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) read(a[i]);
sort(a+1,a+n+1);
n=unique(a+1,a+n+1)-(a+1);
for(int i=1;i<=n;i++)
{
for(int j=0;j<=30;j++)
{
if((a[i]>>j)&1) vis[j][1]=1;
else vis[j][0]=1;
}
}
for(int i=0;i<=30;i++)
{
if(vis[i][0]&&vis[i][1])
{
ans=(1ll<<i)*(n-1);
break;
}
}
cout<<ans<<endl;
return 0;
}
J已经在鹿上了