2022牛客蔚来杯第五场
文章目录
K.Headphones
-
题意
- 总共有n对不同的耳机,已经拿出k对
- 问至少需要再拿多少只耳机才能从n-k对中取出k+1对耳机
-
题解
- 签到题
- 如果剩余的n-k对已经不足以凑出k+1对,那么一定取不出k+1对耳机
- 如果剩余的n-k对数量上足够凑出k+1对,那么考虑取多少只耳机才能保证一定能取出k+1对耳机。剩余2*(n-k)只耳机,最坏情况为把所有的单只耳机取出来了,即取了n-k只耳机,根据鸽巢原理只需再取k+1只耳机一定能保证取出k+1对耳机。即总共需取(n-k)+(k+1)=n+1只耳机
-
代码
#include <iostream>
using namespace std;
int main() {
int n,k;
cin>>n>>k;
if(n-k<k+1) cout<<-1<<'\n';
else cout<<n+1<<'\n';
return 0;
}
B.Watches
-
题意
- 给n个表,给定每个表的价格,有m元钱
- 买表的价格为,手表本身的价钱+购买表的数量*表原始给的顺序i
- 问最多可以买多少个手表
-
题解
- 研究对象是手表的时候,不能确定哪些价值更小,因为k是不确定的
- 研购买究数量k时,k对买表价格的影响是单调的,所以可以二分k。k确定的情况下所有表的价格确定,直接算出价格排序后check前k个能不能买下来即可。
-
代码
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N=1e5+5;
int n,m;
int a[N],b[N];//注意开两个数组,一个存原价值,一个用来check时计算新价值排序的,因为需要check多次,所以只用一个数组不行
bool check(int k) {
for(int i=1;i<=n;i++) b[i]=a[i]+k*i;//计算新价值
sort(b+1,b+1+n);//排序
int res=0;
for(int i=1;i<=k;i++) {//检验是否能买下
res+=b[i];
if(res>m) return false;
}
return true;
}
void solve() {
for(int i=1;i<=n;i++) cin>>a[i];
int l=0,r=n;
while(l<r) {//二分k
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<r<<'\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>m) solve();
return 0;
}
H.Cutting Papers
-
题意
- 求下两式图形的并集面积
{ ∣ x ∣ + ∣ y ∣ + ∣ x + y ∣ < = n x 2 + y 2 = n 2 4 \begin{cases} |x|+|y|+|x+y|<=n\\ x^2+y^2=\frac{n^2}{4} \end{cases} {∣x∣+∣y∣+∣x+y∣<=nx2+y2=4n2
- 求下两式图形的并集面积
-
题解
- 如图,为半个圆+两个小正方形的面积
- 如图,为半个圆+两个小正方形的面积
-
代码
#include<bits/stdc++.h>
using namespace std;
const double pi=3.1415926535897932385;
//可以写成 pi=acos(-1);
int main() {
long long n;
while(cin>>n)
printf("%.12f\n", n * n * pi / 8.0 + n * n / 2.0);
return 0;
}
D.Birds in the tree
-
题意
- 给01字符串表示一棵树所有节点的颜色,以及树的边
- 求有多少个连通子图使得度为1的节点颜色相同
-
题解
-
树形dp
-
f[u,0/1]表示以u为根的子树,u在联通块内,联通块内度数为1的颜色为0/1的联通块数量
-
假设u点颜色为0(1一样)
f [ u , 0 ] = ∏ v ∈ c h i l d u ( f [ v , 0 ] + 1 ) f [ u , 1 ] = ∏ v ∈ c h i l e u ( f [ v , 1 ] + 1 ) − ∑ v ∈ c h i l d u f [ v , 1 ] − 1 f[u,0]=\prod_{v\in child_{u}}(f[v,0]+1)\\ f[u,1]=\prod_{v\in chile_{u}}(f[v,1]+1)-\sum_{v\in child_{u}}f[v,1]-1 f[u,0]=v∈childu∏(f[v,0]+1)f[u,1]=v∈chileu∏(f[v,1]+1)−v∈childu∑f[v,1]−1 -
状态转移方程解释:
f[u,0],对于根节点u的所有孩子都可以选或者不选,即每个孩子对答案的贡献为f[v,0]+1,由乘法原理可以得到上式。并且因为u的颜色为0,所以即使所有孩子都不选联通块只剩u这一个1度的节点也是符合要求的。
f[u,1],一样对于孩子节点可以选和不选然后乘法原理,但是要减去只剩自己的情况,因为颜色不对。同时排除有根节点只有1度的情况,因为根节点不符合颜色要求,所以-1*f[v,1],(排除如下图所示的情况)
-
a n s = ∑ i = 1 n ( f [ i , 1 ] + f [ i , 0 ] ) ,从底向上算, d f s 时实则是这样算 ans=\sum_{i=1}^{n}(f[i,1]+f[i,0]),从底向上算,dfs时实则是这样算 ans=i=1∑n(f[i,1]+f[i,0]),从底向上算,dfs时实则是这样算
-
-
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N=3e5+10,mod=1e9+7;
typedef long long LL;
char s[N];
int n,pos[N];
int h[N],e[2*N],ne[2*N],idx;//注意无向边的要开两倍范围
LL ans,f[N][2];//答案,动态数组
void add(int a,int b) {
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa) {//因为加边会加入父亲节点,而题目要求的是子树,所以传入父节点用来排除计算了父节点
f[u][0]=f[u][1]=1;//初始化为1,才能计算
LL sum=0;
for(int i=h[u];~i;i=ne[i]) {//遍历所有子节点
int j=e[i];
if(j!=fa) {
dfs(j,u);//递归计算
sum=(sum+f[j][pos[u]^1])%mod;
f[u][0]=(f[u][0]*(1+f[j][0]))%mod;//乘法原理
f[u][1]=(f[u][1]*(1+f[j][1]))%mod;
}
}
f[u][pos[u]^1]--;//因为一开始预支了1给这个不符合的,所以再减回来
ans=(ans+f[u][0]+f[u][1]-sum+ mod) % mod;//因为上面-1过,回溯回去计算的时候,相当于u这个子节点不选的方案数已经减掉了,所以不需要f[u][pos[u]^1]-sum,但是ans是全局答案,所以需要再减sum
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;cin>>s+1;
for(int i=1;i<=n;i++) pos[i]=s[i]-'0';//记录每个点的颜色
int a,b;
memset(h,-1,sizeof h);//初始化表头
for(int i=1;i<n;i++) {//加边
cin>>a>>b;
add(a,b);add(b,a);//无向边
}
dfs(1,1);//从根开始算
cout<<ans<<'\n';
return 0;
}
C.Bit Transmission
-
题意
- 一个长度为n的01字符串,询问3n次问某个位置是否为1并得到答案
- 回答中最多会出现一次错误的答案,问能否确定这个字符串
-
题解
-
对每一个位置考虑能否确定字符是什么,对于某位的YES和NO的数量分类讨论
N\Y数量 0 1 >1 0 -1 1/-1 1 1 0/-1 -1 1 >1 0 0 -1 -
-
可确定字符四种情况
Y=1,N>1;确定yes是说谎,所以字符为’0’
Y>1,N=1;同上,字符为’1’
Y>1,N=0;一定没有谎,所以答案为’1’
Y=0,N>1;同上,字符为‘0’
-
不可确定字符的三种情况
Y=0,N=0;没有任何信息,不可能确定
Y=1,N=1;说谎了,不知道是哪个,不可能确定
Y>1,N>1;谎言数量>1了,不符合题意,无法确定
-
可能可以确定的两种情况
Y=1,N=0;如果所有位置判断完之后说有过谎,那么可以确定这位字符为’1’
Y=0,N=1;同理
这种情况没办法在线判断,只能完全判断之后再来判断。所以可以先认为能判断字符是什么,同时记录这种情况有没有出现,最终如果(没说过谎言&&出现过这种情况),无法确定字符
-
-
-
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct bit {
int y, n;
}a[N];
bool ans[N];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
int x;
string s;
for (int i = 1;i <= 3 * n;i++) {
cin >> x >> s;
if (s == "YES") a[x].y++;
else a[x].n++;
}
bool fault = false,have=false;
bool ok = true;
for (int i = 0;i < n;i++) {
if (a[i].y > 1 && a[i].n == 0) ans[i] = 1;
else if (a[i].y == 0 && a[i].n > 1) ans[i] = 0;
else if (!fault && a[i].y > 1 && a[i].n == 1) ans[i] = 1, fault = 1;
else if (!fault && a[i].y == 1 && a[i].n > 1) ans[i] = 0, fault = 1;
else if (a[i].y == 1 && a[i].n == 0) ans[i] = 1,have=true;
else if (a[i].y == 0 && a[i].n == 1) ans[i] = 0,have=true;
else ok = false;
}
if (!ok || !fault&&have) cout << -1;
else for (int i = 0;i < n;i++) cout << ans[i];
cout << '\n';
return 0;
}
G.KFC Crazy Thursday
-
题意
- 回文串模版题
-
题解
- 板子如下,不会,捏吗
-
代码
#include<bits/stdc++.h>
#define MAXN 500010
using namespace std;
int n, tot = 1, last, cur, pos;
int len[MAXN], num[MAXN], fail[MAXN], trie[MAXN][26], ans[4];
char s[MAXN];
int getfail(int now,int i){
while(i - len[now] - 1 < 0 || s[i-len[now]-1] != s[i]) now = fail[now];
return now;
}
int main(){
while(~scanf("%d%s", &n, s)){
memset(ans, 0, sizeof(ans));
memset(num, 0, sizeof(num));
memset(len, 0, sizeof(len));
memset(trie, 0, sizeof(trie));
memset(fail, 0, sizeof(fail));
tot = 1; last = 0; cur = 0; pos = 0;
fail[0] = 1; len[1] = -1;
for(int i = 0; i <= n - 1; i++){
pos = getfail(cur,i);
if(!trie[pos][s[i]-'a']){
fail[++tot] = trie[getfail(fail[pos],i)][s[i]-'a'];
trie[pos][s[i]-'a'] = tot;
len[tot] = len[pos] + 2;
num[tot] = num[fail[tot]] + 1;
}
cur = trie[pos][s[i]-'a'];
last = num[cur];
if(s[i] == 'k') ans[1] += last;
else if(s[i] == 'f') ans[2] += last;
else if(s[i] == 'c') ans[3] += last;
}
printf("%d %d %d\n", ans[1], ans[2], ans[3]);
}
return 0;
}