牛客周赛 Round 68以补完
A三途川的摆渡人(二)
https://ac.nowcoder.com/acm/contest/96808/A
对着题目意思写就好
void tt()
{
int n;
string s;
cin >> n >> s;
int ans = 0;
for (int i = 0; i < n; i++)
if (s[i] == '0')
ans++;
cout << ans << endl;
}
B魔法之森的蘑菇(二)
https://ac.nowcoder.com/acm/contest/96808/B
纯暴力也能写 我用的前缀和优化,就是用一个二维前缀和判断里面是否全为0
int a[35][35];
void tt()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n;i++)
{
string s;
cin >> s;
for (int j = 1; j <= m;j++)
{
if(s[j-1]=='*')
a[i][j] = 1;
}
}
for (int i = 1; i <= n;i++)
for (int j = 1; j <= m;j++)
{
a[i][j] += a[i - 1][j] + a[i][j - 1];
a[i][j] -= a[i - 1][j - 1];
}
int ax1=0, ax2=0, ay1=0, ay2=0;//记录答案
int s = 0;//记录当前最大面积
for (int i = 1; i <= n;i++)
for (int j = 1; j <= m;j++)
for (int l = i; l <= n;l++)
for (int r = j; r <= m;r++)
{
int t = a[l][r] - a[i - 1][r] - a[l][j - 1] + a[i - 1][j - 1];
if(t)continue;
if((r-j+1)*(l-i+1)>s&&(r-j+1)*(l-i+1))//算距离时记得加1
{
ax1 = i, ax2 = l, ay1 = j, ay2 = r;
s = (r - j + 1) * (l - i + 1);
}
}
cout << ax1 << " " << ay1 << " " << ax2 << " " << ay2 << endl;
}
C迷途之家的大贤者(二)
https://ac.nowcoder.com/acm/contest/96808/C
思维题,据说是出题人得意之题
对于两个数组来说可能会用重复的数,我们先用set去重 然后用n减去set的大小 可以得到两个数 计小的为mi,大的为mx,然后此时还有一些两个数组中都存在的数,在用cnt记录一下,现在就得到三个数 mi,mx,cnt,假如说小红的数组得到的数是mi 那么先看看小紫去完重后小红能否把小红和小紫的set中都存在的数去除掉(就是小红去完重了,小紫还在去重,小红就去将两人公共的数消除),就是看mi+cnt和mx谁大 如果mx大小红就只能等着等小紫去重完,否则如果mi+cnt大小紫就可以也来帮着消除公共的数,记得向上取整(如果剩下一个也得有人去消另一个人随便消一个)
void tt()
{
int n;
cin >> n;
set<int> st1, st2;
for (int i = 1; i <= n;i++)
{
int x;
cin >> x;
st1.insert(x);
}
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
st2.insert(x);
}
int mi = n - max(st1.size(), st2.size());
int mx = n - min(st1.size(), st2.size());
int cnt = 0;
for(auto t:st1)
if(st2.count(t))
cnt++;
if(cnt+mi<=mx)
cout << mx << endl;
else
cout << mx + (cnt + mi - mx + 1) / 2 << endl;
}
D红魔馆的馆主(二)
https://ac.nowcoder.com/acm/contest/96808/D
可以只看3 3 5 11 这几个数,可以可以完全暴力 $n*\sqrt[2]{n} $
用数组cnt[i]记录有多少个数可以取余i为0,用vector
然后对于每个数x来说,它可以跟数组中其他数相乘为495的倍数的个数就是cnt[495/__gcd(495,x)],就是x提供了__gcd(x,495),然后找数组中的另一个数来提供495/__gcd(x,495),如果一个数的余数495/__gcd(x,495),那它和x相乘必定是495的倍数(算之前记得把这个数的余数先去掉,因为不可以选同一个数),先算出来不加一有多少个,在用mx记录如果把这个数加一可以增加多少
int a[N];
vector<int> d[N];
int cnt[N];
int qu(int x)
{
int t = __gcd(495ll, x);
return cnt[495 / t];
}
void tt()
{
int n;
cin >> n;
for (int i = 1; i <= n;i++){
cin >> a[i];
int t = sqrt(a[i]);
for (int j = 1; j <= t;j++){
if(a[i]%j==0){
cnt[j]++, d[i].push_back(j);
if(a[i]/j!=j){
cnt[a[i] / j]++, d[i].push_back(a[i] / j);
}
}
}
}
//先处理余数和cnt
int ans = 0, mx = 0;
for (int i = 1; i <= n;i++){
for(auto t:d[i])
cnt[t]--;//先把这个数的余数去掉
ans += qu(a[i]);
mx = max(mx, qu(a[i] + 1) - qu(a[i]));//算如果这个数加一可以增加多少方案
for (auto t : d[i])
cnt[t]++;
}
cout << ans / 2 + mx << endl;//ans记得除2,因为算了两次x和y算了一次 y和x又算了一次
}
E博丽神社的巫女(二)
https://ac.nowcoder.com/acm/contest/96808/E
dp,据说是之前有场周赛的简化版
用dp来记录是否可以到达,用数组res记录每个数贡献多少去组成1e5
就是对于数组中的每一个数,每个数都可以提供一部分(0-a[i])看能不能组成1e5
如果能,就从res[n][100000]开始一直到第一个数去算出到达这个贡献要多少次除2
代码有解释
const int N = 100005;
vector<int> a(N);
int dp[105][N]; // 记录是否可以走到
int res[105][N]; // 记录需要提供多少
int ans[105];
void tt()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
dp[0][0] = 1;初始话
// 每组只能选一个 组成1e5
for (int i = 1; i <= n; i++){
set<int> st;
int x = a[i];
while (x)
st.insert(x),x /= 2;//将这个可以贡献的价值用set储存起来
for (int j = 1; j <= 100000; j++){
dp[i][j] = dp[i - 1][j];//如果说前面已经可以组成j了那么第i个数贡献就直接为0
if (dp[i][j])
continue;
for (auto t : st)
if (j >= t && dp[i - 1][j - t])
dp[i][j] = 1,res[i][j] = t;
}
}
if (!dp[n][100000]){
cout << -1 << endl;
return;
}
int now = 100000;
int i = n;
while (i > 0){
if (res[i][now]){//如果有贡献就开始算
int cnt = 0;
while (a[i] != res[i][now])
a[i] /= 2, cnt++;//判断它要进行多少次除2
ans[i] = cnt;
now -= res[i][now];
}
else//没有就直接取一个向上取整的log
ans[i] = ceil(log2(a[i]));
i--;
}
for (int i = 1; i <= n; i++)
cout << ans[i] << " ";
}
F雾之湖的冰精(三)
https://ac.nowcoder.com/acm/contest/96808/F
树形dp
假如以1为根节点,从叶子节点一直往上走,当遇到一个节点时,算它的以该点为端点的简单路径(用加法原理),在算一部分以它为中间节点的简单路径(用乘法原理)
代码有解释
const int N = 100005;
int a[N], dp[N][10];
vector<int>g[N];
int ans = 0;
void dfs(int fa,int now)//树形dp
{
if(g[now].size()==1&&fa!=-1){//遇到叶子节点
dp[now][a[now]] = 1;
return;
}
for(auto t:g[now]){
if(t==fa)continue;
dfs(now, t);
int ed = 9 - a[now];//从下往上传最多能传多少
vector<int> res(10, 0);//先用一个数组储存起来
for(int i = 0; i <= ed;i++){//从下面传i上来,以now为端点
res[i] += dp[t][i];
ans += dp[t][i];
}
for (int i = 0; i <= ed;i++){//以now为中间节点
for (int j = a[now]; j + i <= 9;j++){
ans += res[i] * dp[now][j];
}
}
for (int i = 0; i <= ed;i++)
dp[now][i + a[now]] += res[i];//算完后给该点加上
}
dp[now][a[now]]++;//记得最后往上走到时候加上以该点为起点的简单路径
}