CF1070
CF1070
注:这次CF是和 @Terdy 合作进行的,Ta的博客链接,这里只进行偶数题号。
CF1070B
CF1070B题意
联邦通信、信息技术和大众传媒监督局(Berkomnadzor)是伯利兹联邦执行机构,负责保护伯利兹普通居民免受现代互联网的威胁。
Berkomnadzor 拥有一份禁止使用的 IPv4 子网名单(黑名单)和一份允许使用的 IPv4 子网名单(白名单)。柏克兰的所有互联网服务提供商(ISP)都必须配置网络设备,阻止访问符合黑名单的所有 IPv4 地址。同时,ISP 必须允许(即不阻止)所有符合白名单的 IPv4 地址访问。如果某个 IPv4 地址与上述两个列表都不匹配,则由 ISP 决定是否阻止该地址。当且仅当一个 IPv4 地址与黑名单(白名单)中的某个子网相匹配时,它才会与黑名单(白名单)相匹配。一个 IPv4 地址可以同时属于白名单和黑名单,这种情况会导致矛盾(见输出描述中的无解情况)。
IPv4 地址是一个 32 位无符号整数,书写形式为 \(a.b.c.d\),其中每个值 $a,b,c,d $ 称为一个八位位组,是一个从 $0 $ 到 $255 $ 的十进制整数。例如,IPv4 地址 $ 192.168.0.1 $ 可以用以下表达式转换为 32 位数字 $ 192 \cdot 2^{24} + 168 \cdot 2^{16} + 0 \cdot 2^8 + 1 \cdot 2^0 $ 。第一个八位位组 $ a $ 编码最有意义(最左边的 $ 8 $ 位),八位位组 $ b $ 和 $ c $ 下面的 $ 8 $ 位块次有意义(按此顺序),八位位组 $ d $ 编码最无意义(最右边的 $ 8 $ 位)。
伯兰的 IPv4 网络与世界其他地方略有不同。伯兰没有保留地址或内部地址,而是使用所有可能的 $ 2^{32} $ 值。
一个 IPv4 子网用 $ a.b.c.d $ 或 $ a.b.c.d/x $ 表示(其中 $ 0 \le x \le 32 $ )。子网 $ a.b.c.d $ 包含一个地址 $ a.b.c.d $ 。一个子网 $ a.b.c.d/x $ 包含所有最左边(最重要)位 $ x $ 等于地址 $ a.b.c.d $ 最左边位 $ x $ 的 IPv4 地址,要求子网 $ a.b.c.d/x $ 最右边(最不重要)位 $ 32 - x $ 为 0。
与子网 $ a.b.c.d/x $ 匹配的所有地址自然会形成一个连续的范围。该范围从地址 $ a.b.c.d $ 开始(其最右边的 $ 32 - x $ 位为零)。该范围以地址 $ a.b.c.d $ 的最左端 $ x $ 位等于地址 $ a.b.c.d $ 的最左端 $ x $ 位,且其最右端 $ 32 - x $ 位全为 1 的地址结束。子网恰好包含 $ 2^{32-x} $ 地址。子网 $ a.b.c.d/32 $ 恰好包含一个地址,也可以只用 $ a.b.c.d $ 表示。
例如,子网 $ 192.168.0.0/24 $ 包含 256 个地址。$ 192.168.0.0 $ 是地址范围的第一个地址,$ 192.168.0.255 $ 是最后一个地址。
Berkomnadzor 的工程师制定了一项提高 Berland 全球网络性能的计划。他们不想同时维护白名单和黑名单,而只想建立一个包含最少子网数量的优化黑名单。这样做的目的是阻止所有符合优化黑名单的 IPv4 地址,并允许所有其他地址访问。当然,旧黑名单中的 IPv4 地址必须继续封锁,而旧白名单中的所有 IPv4 地址必须继续允许。那些既不符合旧黑名单也不符合旧白名单的 IPv4 地址,无论其以前是否可以访问,都可以被阻止或允许。
请编写一个程序,将黑名单和白名单作为输入,并生成优化黑名单。优化后的黑名单必须包含尽可能少的子网,并满足上述所有 IPv4 地址可访问性要求。
源列表中的 IPv4 子网可以任意交叉。如果某个 IPv4 地址同时符合源白名单和黑名单,请输出一个数字-1。
CF1070B题解
CF1070B代码
CF1070D
CF1070D题意
Vasya已经很多次忘记将垃圾从公寓里扔出去。现在他想制定一个计划并遵守它。
对于接下来的n天Vasya知道他每天会制造ai的垃圾。Vasya会将垃圾放在垃圾袋中并扔掉垃圾袋每个袋子能装k的垃圾,一天可以扔掉或使用多个垃圾袋。
Vasya想要使用最少的垃圾袋,你需要输出最少的垃圾袋数量。
当天生产的垃圾是可以留到第二天的,但最多留到第二天。
CF1070D题解
发现每次先用前一天的垃圾构成垃圾袋,然后在装今天的,有剩余的扔给明天。
特判下边界即可。
CF1070D代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
int n, k;
signed main(){
n = read();k = read();int lst = 0, ans = 0;
for(int i = 1;i <= n;i++){
int x = read();
if(x + lst <= k && lst != 0){ans++;lst = 0;continue;}
else{
if(lst){x -= (k - lst);ans++;}
ans += x / k;lst = x % k;
}
}
if(lst)ans++;
printf("%lld\n",ans);
return 0;
}
CF1070F
CF1070F题意
Alice 和 Bob 参加选举。
有 \(n\) 个投票人,第 \(i\) 个人有两个属性 \(s_i\) 和 \(a_i\) 。
对于第 \(i\) 个人, \(s_i\) 是一个长度为 \(2\) 的 \(01\) 串:
\(s_i=00\) 表示第 \(i\) 个人不支持 Alice 和 Bob 。
\(s_i=01\) 表示第 \(i\) 个人不支持 Alice 但支持 Bob 。
\(s_i=10\) 表示第 \(i\) 个人支持 Alice 但不支持 Bob 。
\(s_i=11\) 表示第 \(i\) 个人支持 Alice 和 Bob 。
第 \(i\) 个人的影响力为 \(a_i\) 。
现在要从中选出一些人,满足下面 \(2\) 个条件:
(1)选出的人中,支持 Alice 的人数的 \(2\) 倍大于或等于选出的人数。
(2)选出的人中,支持 Bob 的人数的 \(2\) 倍大于或等于选出的人数。
求选出的人的影响力之和的最大值。
CF1070F题解
发现11和配对的10,01可以直接选,剩下的01(10)和00满足条件了就选,否则不选。
CF1070F代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
priority_queue<int,vector<int>,less<int> > que1,que2, que3;
int n;
signed main(){
n = read();int ans = 0,cnt[3] = {0,0,},tot = 0;
for(int i = 1;i <= n;i++){
int opt = read(), x = read();
if(opt == 00){que3.push(x);}
if(opt == 11){ans += x;cnt[1]++;cnt[2]++;tot++;}
if(opt == 10){que1.push(x);}
if(opt == 01){que2.push(x);}
}
while(!que1.empty() && !que2.empty()){ans += que1.top() + que2.top();que1.pop();que2.pop();cnt[1]++;cnt[2]++;tot += 2;}
int opt = 1;
if(que1.empty()){que1 = que2;opt = 2;}
while(!que1.empty() || !que3.empty()){
int x = 0;
if(!que1.empty() && cnt[3 - opt] * 2 >= tot + 1){
x = que1.top();
}
int y = 0;
if(!que3.empty() && cnt[1] * 2 >= tot + 1 && cnt[2] * 2 >= tot + 1){
y = que3.top();
}
if(!x && !y)break;
else{
if(x > y){ans += x;que1.pop();cnt[opt]++;tot++;}
else{ans += y;que3.pop();tot++;}
}
}
printf("%lld\n",ans);
return 0;
}
CF1070H
CF1070H题意
开始给你\(n\)个由小写字母、数字、以及'.'(点号)构成的字符串\((n\le 10000,len\le 8)\),后有\(q(q\le 50000)\)组询问,询问上述字符串中有多少串中包含给出字符串的子串,并输出其中的任意一个串。如果不存在输出0 -。
CF1070H题解
直接拿map艹过去就完了。
CF1070H代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
map<string,int> mp;
map<string,string> mp1;
int n, q;
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n;string str;
for(int i = 1;i <= n;i++){
cin >> str;vector<string> vec;vec.clear();
for(int l = 0;l < str.size();l++)
for(int r = l;r < str.size();r++)
vec.push_back(str.substr(l,r - l + 1));
sort(vec.begin(),vec.end());vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(auto i : vec){mp[i]++;mp1[i] = str;}
}
cin >> q;
for(int i = 1;i <= q;i++){
cin >> str;
if(mp.count(str))cout << mp[str] << ' ' << mp1[str] << '\n';
else cout << "0 -\n";
}
return 0;
}
CF1070J
CF1070J题意
有 \(m\) 条横线,\(n\) 条竖线,共有 \((n\times m)\) 个交叉点.
\(k(m+n\le k)\)个名字(每个名字是一个大写字母),现在要给这 \((m+n)\) 条线命名,若构成一个交叉点的两条直线的名字相同,则这个交叉点不美丽。
问不美丽的交叉点的最少个数是多少。
多组数据,第一行 \(t\)(数据组数)
\((1\le t\le 30000)\)
每组数据依次为 \(n,m,k\),第二行是名称.
\((1\le n,m\le 30000,n+m\le k\le2∗10^5)\)
CF1070J题解
发现重复的字母最多一种,然后就暴力枚举这个重复的字母,每次DP下能够取到的数字即可。
CF1070J代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e5 + 10;
int n, m, k;
int cnt[30];
char ch[maxn];
bool f[maxn];
void solve(){
n = read(); m = read(); k = read();
if(n < m)swap(n, m);
for(int i = 0;i < 30;i++)cnt[i] = 0;
scanf("%s",ch + 1);
for(int i = 1;i <= k;i++)cnt[ch[i] - 'A']++;
int ans = 0x3f3f3f3f, rest = k - m - n;
vector<int> tmp;tmp.clear();tmp.push_back(0);
for(int i = 0;i < 26;i++)if(cnt[i])tmp.push_back(cnt[i]);k = tmp.size() - 1;
for(int i = 1;i <= k;i++){
f[0] = 1;for(int i = 1;i <= n;i++)f[i] = 0;
for(int j = 1;j <= k;j++){
if(i == j)continue;
for(int l = n;l >= tmp[j];l--)f[l] |= f[l - tmp[j]];
}
// for(int j = 1;j <= n;j++)printf("%d ",f[j]);puts("");
for(int j = min(tmp[i],n);j >= 0;j--){
if(!f[n - j])continue;
ans = min(ans,j * max(0,tmp[i] - j - rest));
}
}
printf("%d\n",ans);
}
signed main(){
int T = read();
while(T--){solve();}
return 0;
}
CF1070L
CF1070L题意
给一个 \(n\) 个点 \(m\) 条边的无向图,你需要将这 \(n\) 个点划分为 \(r\) 个集合,满足条件:
- 每个点集的导出子图中所有点度数均为偶数。
求出最小的 \(r\) 并输出任意一种划分方案。
CF1070L题解
猜测答案只有 \(1,2\) 两种。
等会证明正确性,先说解法。
当所有点的度数都是偶数的时候只需要一个集合,我们只讨论存在奇数情况。
先分类讨论,设 \(s_u\) 表示 \(u\) 被分为哪个集合 \((0/1)\):
- \(s_u=0\)
- \(d_u\%2=0\):那么周围归为一集合的点应为偶数,即 \(s_u \oplus (\oplus_{(u,v)\in E} s_v) = 0\)
- \(d_u\%2=1\):那么周围归为一集合的点应为奇数,即 \(s_u \oplus (\oplus_{(u,v)\in E} s_v) = 1\)
- \(s_u=1\)
- \(d_u\%2=0\):那么周围归为一集合的点应为偶数,即 \(s_u \oplus (\oplus_{(u,v)\in E} s_v) = 1\)
- \(d_u\%2=1\):那么周围归为一集合的点应为奇数,即 \(s_u \oplus (\oplus_{(u,v)\in E} s_v) = 0\)
综上所述,得,\(s_u \oplus (\oplus_{(u,v)\in E} s_v) = [d_u\%2]\)。
这个东西显然可以高斯消元,然后用bitset优化一下就好了。
等等,等等,你还没说为啥答案只有这两种呢?
如果想让答案超过二,那么上面这个式子一定出现无解的情况,也就是说,出现\(0=1\)的情况。
\(0\) 可以表示为所有变量出现在式子中的次数为偶数,\(1\) 可以表示为这些变量代表的点度数和为奇数。
显然无向图的任何一个子图度数和都是偶数,故这种情况不可能出现。
CF1070L代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2048;
bitset<maxn> f[maxn];
int n, m, d[maxn];
bool ans[maxn];
void solve(){
n = read(); m = read();int u, v, cnt = 1;
for(int i = 1;i <= n;i++){f[i].reset();d[i] = 0;ans[i] = 0;}
for(int i = 1;i <= m;i++){
u = read(); v = read();
f[u].set(v); f[v].set(u);
d[u]++; d[v]++;
}
for(int i = 1;i <= n;i++)cnt &= (!(d[i] & 1));
if(cnt){puts("1");for(int i = 1;i <= n;i++){putchar('1');putchar(' ');}puts("");return;}
for(int i = 1;i <= n;i++){
f[i][n + 1] = f[i][i] = (d[i] & 1);
}
for(int i = 1;i <= n;i++){
for(int j = i;j <= n;j++)
if(f[j][i]){swap(f[j],f[i]);break;}
for(int j = i + 1;j <= n;j++)
if(f[j][i]){f[j] ^= f[i];}
}
// for(int i = 1;i <= n;i++){
// for(int j = 1;j <= n + 1;j++)
// putchar('0' + f[i][j]);
// puts("");
// }
puts("2");
for(int i = n;i;i--){
ans[i] = f[i][n + 1];
for(int j = i + 1;j <= n;j++){
ans[i] ^= (f[i][j] & ans[j]);
}
}
for(int i = 1;i <= n;i++){putchar('1' + ans[i]);putchar(' ');}
puts("");
}
signed main(){
int T = read();
while(T--)solve();
return 0;
}

浙公网安备 33010602011771号