总之就是 | ZROI CSP连测 Day2
「0.0」序言
虽然这个题解发的比 NOIP Day2 的晚,但是实际考试时间是比那个早的。
虽然第一题挂了 \(30\) pts,但是总分还算可以。也是这几次模拟赛唯一一次涨 rating。
题目还是 根 据 实 际 情 况 写个题目。
缺省源可以去这篇博客拿,当然底下有些用了什么 unordered_map
和 bitset
之类的头文件自己加上就好了w
「1.0」签到挂分好耶
一个合法的 IP 地址是由四个不含前导 \(0\) 且不大于 \(255\) 的四个数字和三个 .
组成的。
现在给出一个字符串,判断它是不是合法的地址,并将修改后的合法地址输出。(保证字符串中存在四个分开的自然数)
「1.1」思路简述
众所周知我们有快读,于是直接对快读改一改即可,但是快读最后实际上会多读一个字符,所以还要判断一下最后那个多读的字符。
「1.2」Code
bool flg;int cntdot;
template<typename J>
I void fr(J &x)
{
short f(1);x=0;char c(getchar());
while(c<'0' or c>'9')
{
if(x!=0)
{
flg=1;
break;
}
c=getchar();
if(c=='.') ++cntdot;
else if(c!='.' and c>='0' and c<='9') flg=1;
}
while (c>='0' and c<='9')
{
if(x==0 and c=='0') flg=1;
x=(x<<3)+(x<<1)+(c^=48);
if(x>=255) {x=255;flg=1;}
c=getchar();
if(c=='.') ++cntdot;
else if(c!='.' and c>='0' and c<='9') flg=1;
}
x*=f;
}
template<typename J>
I void fw(J x,bool k)
{
if(x<0) x=-x,putchar('-');
static short stak[35];short top(0);
do
{
stak[top++]=x%10;
x/=10;
}
while(x);
while(top) putchar(stak[--top]+'0');
k?puts(""):putchar('.');
}
I void Files()
{
freopen("RNMTQ.in","r",stdin);
freopen("RNMTQ.out","w",stdout);
}
S main()
{
LL x,y,j,k;
fr(x),fr(y),fr(j),fr(k);
if(cntdot>=3) flg=1;
if(flg) puts("NO");
else puts("YES");
fw(x,0),fw(y,0),fw(j,0),fw(k,1);
Heriko Deltana;
}
「2.0」贪心正解好耶
给出一个长度为 \(n\) 且只有 \(A\) 和 \(P\) 的字符串。每次操作你能将所有形如 \(AP\) 和 \(PP\) 的组合删掉,问若干次操作之后字符串最短是多少。
「2.1」思路简述
这个题我最一开始想的就是贪心,而且感觉贪心很对。但是隔壁都在考虑区间 DP,于是我就非常不解,于是我就看着他们自己构造数据把自己写的 DP 卡掉了但是卡不掉贪心。
然后大家就都交的贪心,但是贪心确实是正解w
发现整个字符串中只有 \(P\) 能造成操作,所以我们在读入的时候判断一下 \(P\)。又因为字符串中的 \(A\) 只能又 \(AP\) 这个组合删去,所以我们贪心的先删掉 \(AP\)。
「2.2」Code
CI MXX=10005;
char c;
int n,cnta,cntp;
S main()
{
ON;
while(cin>>c)
{
if(c=='A') ++cnta;
else if(c=='P')
{
if(cnta) --cnta;
else ++cntp;
}
}
cout<<cnta+(cntp&1);
Heriko Deltana;
}
「3.0」祖孙限定好耶
现在有 \(n\) 条指令,且每个语句都要声明一个类,这里类的定义和 C++ 中的类相似。
每个类的声明语句格式为:\(\tt ClassName : A_1,A_2,A_3,A_4,\cdots,A_k ;\)
这个语句的意思是 \(\tt{ClassName}\) 继承自 \(\tt A_1,A_2,\cdots,A_k\)。当然,一个类也可以不继承自其它类。
如果 \(\tt C_1\) 继承自 \(\tt C_2\),\(\tt C_3\) 继承自 \(\tt C_3\),……,\(\tt C_{k-1}\) 继承自 \(\tt C_k\),那么就称 \(\tt C_1,\cdots,C_{k-1}\) 都派生自 \(\tt C_k\)。
在声明时不能出现以下的情况:
-
继承的类未被声明。
-
本次声明的类已经被声明过。
-
声明的类之间出现了菱形关系。
菱形关系如下图,其中:
\(X\) 和 \(Y\) 派生自 \(A\);
\(B\) 派生自 \(X\) 和 \(Y\);
\(X\) 和 \(Y\) 之间没有派生关系。
若出现以上情况,本次声明的类会被抛弃。
现在要求判断每条语句是否合法。
「3.1」思路简述
显然不合法的前两种情况用一个 map
就能解决掉。
现在来考虑第三种。实际上菱形关系就是指从多个近亲派生,即禁止近亲〇,除非是祖孙关系(也就是说禁止从多个旁系派生)。
如果当前语句合法,那么它所继承的类的旁系亲属都是它的旁系亲属,于是就可以用 bitset
去维护。(bool
数组维护的时间复杂度是 \(O(n^3)\),而 bitset
是 \(O(\dfrac{n^3}{w})\),而且我们正好需要位运算。)
「3.2」Code
CI MXX(1005);
unordered_map<string,int> mp;
bitset<1005> f[MXX],b[MXX];
int n,cnt,tot,q[MXX];
S main()
{
ON;cin>>n;
while(n--)
{
bool wa(0),flg(0);tot=0;string s,c;
cin>>s>>c>>c;
while(c!=";")
{
if(!mp[c]) wa|=1;
q[++tot]=mp[c];cin>>c;
}
bitset<1005> temp;
for(int i(1);i<=tot;++i) temp[q[i]]=1;
for(int i(1);i<=tot;++i)
if((temp&b[q[i]]).any())
flg=1;
if(flg) wa|=1;
if(wa or mp[s]) {cout<<"greska\n";continue;}
cout<<"ok\n";mp[s]=++cnt;f[cnt][cnt]=1;
for(int i(1);i<=tot;++i) f[cnt]|=f[q[i]];
for(int i(1);i<=cnt;++i)
if(!f[cnt][i] and (f[i]&f[cnt]).any())
b[cnt][i]=1;
}
Heriko Deltana;
}
「4.0」读不懂题好耶
本题
Code
我是真的不想写了,我懒承受不住。题面基本和原题面一致(因为实在不会简化了w
某人的研究兴趣是在图 \(G=(V(G),E(G))\) 中找到最好的 k-degree 子图。
子图 \(S\) 是 \(G\) 的 k-degree 子图需要满足以下要求:
每个顶点 \(v(v \in S)\) 在 \(S\) 中至少有 \(k\) 度;
-
\(S\) 是连通的 ;
-
\(S\) 是极大的,即 \(S\) 的任何超图都不是 k-degree 子图,除了 \(S\) 本身。
如下图:
然后那人就定义子图 \(S\) 的分数。在定义分数之前,他首先定义:
-
\(n(S)\):子图 S 中的顶点数,即 \(n(S)=|V(S)|\);
-
\(m(S)\):子图 S 的边数,即 \(m(S)=|E(S)|\);
-
\(b(S)\):子图 S 中的边界边数,\(b(S)=|(u,v)|(u,v)∈E(G),u∈V(S),v∉V(S),v∈V(G)|\);
他定义一个子图的分数为 \(score(S)=M⋅m(S)−N⋅n(S)+B⋅b(S)\),其中 \(M,N,B\) 是给定的常数。
子图的分数越高,认为它越好。你需要在图 \(G\) 中找到最好的 k-degree 子图。如果有许多 k-degree 子图的分数相同,则应最大化 \(k\)。输出最大的分数及对应的 \(k\).
「4.1」思路简述
这题目真是给我整异或了,我在场上是真的看不懂,于是暴力也没打。
为了下面描述方便,这里先做一个定义;
- \(G(k)\):代表图 \(G\) 的 k=degree 子图。
不考虑联通性的情况下,\(G(k+1)\) 显然是 \(G(k)\) 的子图,那么我们给不在 \(G(k+1)\) 中,但是在 \(G(k)\) 中的点 \(X\) 求一个标记。
显然这个过程不断迭代就能求得 \(G(k)\) 的值。
下面再定义三个集合:
-
\(E(u,>)\):代表 \(u\) 的边中另一个节点的标记比 \(u\) 大的集合。
-
\(E(u,=)\):代表 \(u\) 的边中另一个节点的标记和 \(u\) 相等但是节点编号大于 \(u\) 的集合。
-
\(E(u,<)\):代表 \(u\) 的边中另一个节点的标记比 \(u\) 小的集合。
那么新加入的点 \(new\) 对 \(n,m,b\) 的贡献就是:
-
\(\Delta n = 1\)
-
\(\Delta m = |E(u,>)| + |E(u,=)|\)
-
\(\Delta b = |E(u,<)| - |E(u,>)|\)
合并过程用并查集去维护,每个连通块的代表元素应当是编号最小的那一个,以及对于当前连通块的 \(X\) 点。
这样的话总体的复杂度(并查集使用路径压缩)是 \(O(m \log n + n)\),如果再加上按秩合并,总体复杂度即为 \(O(m+n)\)。
「5.0」尾声
希望下次不挂分。