牛客挑战赛53题解
A题
题意:进行最少操作从原点走到x处,第k次操作可以选择向右走k步或者向左走1步。
解:先用最少的次数到达>=x,即求出最小的r,r*(r+1)/2 >= x,
此时,可能多走了0-(r-2)步,
考虑在前面的操作中选择一个变成往左走。
比如第1步往左走,最终对答案的影响是-2,以此类推,第i步变成往左走会让答案-(i+1),所以除非r*(r+1)/2 - x == 1,其余的x都可以在r步得到。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x;
int t;
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>t;
while(t--)
{
cin>>x;
ll k = 1LL * (long long)sqrt(x * 2);
if(k * (k + 1) / 2 < x) k++;
ll r = k * (k + 1) / 2 - x;
if(r == 1) k++;
cout<<k<<'\n';
}
return 0;
}
B题
题意:给出s,要求构造一个只包含0,1和质数的序列使得序列的和为s,要求序列长度尽可能小。
解:先来背诵哥德巴赫猜想qaq。
如果本身这个数不是质数。
根据关于偶数的哥德巴赫猜想,任意一个大于2的偶数都可以写成两个素数之和。
然后对于奇数x来说,有两种思路,变成2+(x-2),这样序列就有最多2个数,只需要检查x-2是不是质数就可以,或者变成1+(x-1),因为x-1是偶数,所以序列最多3个数。
因为n只有1e7,预处理出质数暴力查找即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e7;
int p[N + 10];
bool vis[N + 10];
int tot,t,n;
void init()
{
for(int i = 2;i <= N; ++i)
{
if(!vis[i]) p[++tot] = i;
for(int j = 1;j <= tot && p[j] * i <= N; ++j)
{
vis[i * p[j]] = 1;
if(i % p[j] == 0) break;
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
init();
cin>>t;
while(t--)
{
cin>>n;
if(n <= 2 || !vis[n])
{
cout<<"1\n"<<n<<" = "<<n<<'\n';
continue;
}
if(n % 2 == 0)
{
for(int i = 1;i <= tot && p[i] <= n; ++i)
if(!vis[n - p[i]])
{
cout<<"2\n"<<p[i]<<" + "<<n - p[i]<<" = "<<n<<'\n';
break;
}
continue;
}
if(!vis[n - 2])
{
cout<<"2\n2 + "<<n - 2<<" = "<<n<<'\n';
continue;
}
for(int i = 1;i <= tot && p[i] <= n - 1; ++i)
if(!vis[n - p[i] - 1])
{
cout<<"3\n1 + "<<p[i]<<" + "<<n - p[i] - 1<<" = "<<n<<'\n';
break;
}
}
return 0;
}
C题
题意:给出n个点m条边的无向图,求每个集合中有多少个子集是独立集(独立集是指集合中任意两点之间都没有边)。
解:n只有26。dp[i]表示编号为i的集合中有多少子集是独立集,我们设x为i的二进制为1的最低位(即lowbit(i))。
如果选择x不冲突,dp[i]可以由dp[i^(1<<x)]转移过来,
如果选择x冲突,dp[i]可以由dp[i^(i & pw[x)]转移过来,pw记录的是所有x的互斥的点。
最后,又卡时间又卡空间太难受了,被迫写的特别丑才过,复杂度是O(2^n)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p = 1e9 + 7;
int pw[26];
int dp[(1 << 26) + 10];
int pe[(1 << 25) + 10];
int n,m,x,y;
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i = 0;i < n; ++i)
pw[i] = (1 << i),pe[(1 << i)] = i;
while(m--)
{
cin>>x>>y;
pw[x] |= (1 << y);
pw[y] |= (1 << x);
}
dp[0] = 1;
for(int i = 1;i < (1 << n); ++i)
{
int id = pe[i&(-i)];
dp[i] = dp[i ^ (1 << id)] + dp[i ^ (i & pw[id])];
if(dp[i] >= p) dp[i] -= p;
}
ll ans = 0;
for(int i = (1 << n) - 1;i >= 0; --i)
ans = (233LL * ans + dp[i]) % p;
cout<<ans<<'\n';
return 0;
}