11.12
贺了好多道 AT 之后发现自己瞎猜的能力有所提升!!!
11.11
A.[2011福建集训] 相框,Idea Frame
开场二十多分钟猜了个结论,感觉很对。
由于只有一个小样例且题面没说有自环甚至暗示没有自环且数据故意造自环最后挂成了 20 分。
最后环一定是每个点的读书都为 \(2\),所以对于度数大于 \(2\) 的我们要对它进行一次拆,若度数为奇数那么我们会拆出一个度数为 \(1\) 的点,最后答案就是拆的次数加上二分之度数为 \(1\) 的点的个数。
注意特判一下图不连通且有若干连通块为环的情况,这时候我们要把这个环也拆开。
upd:原题不知道为什么挂了,差的还挺多。
corner case 有点多
#include <bits/stdc++.h>
#define fr first
#define se second
#define Un unsigned
#define LL long long
#define pb push_back
#define pii pair<int,int>
#define pLi pair<LL,int>
#define pLL pair<LL,LL>
#define __ __int128
#define ld long double
#define VE vector<LL>
#define db double
#define Ve vector<int>
using namespace std;
inline LL read()
{
LL x = 0,f = 1;char ch = getchar();
while(!isdigit(ch)) (ch == '-') && (f = -1),ch = getchar();
while(isdigit(ch)) x = x*10+ch-48,ch = getchar();
return x*f;
}
const int N = 2e5+5;
int d[N],fa[N],s[N],b[N],siz[N];
bool vis[N];
inline int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
int main()
{
int n = read(),m = read(),tot = n;
for (int i = 1;i <= N-5;i++) fa[i] = i,siz[i] = 1;
for (int i = 1,x,y;i <= m;i++)
{
x = read(),y = read();
if (!x) x = ++n,tot++;
if (!y) y = ++n,tot++;
d[x]++,d[y]++;
x = find(x),y = find(y);
if (x != y) fa[x] = y,siz[y] += siz[x],tot--;
}
for (int i = 1,x;i <= n;i++)
{
x = find(i),s[x] += (d[i] > 2),b[x] += (d[i]&1);
if (siz[x] == 1 && !d[i]) tot--;
}
int ans = 0,cnt = 0;
for (int i = 1;i <= n;i++)
{
int x = find(i);
if (vis[x]) continue;
if (siz[x] == 1)
{
if (!d[x]) continue;
b[x] += 2;if(d[x] == 2) ans++;
}
vis[x] = 1;ans += s[x];
if (!b[x] && tot > 1)
{
if(!s[x]) ans++;
b[x] += 2;
}
cnt += b[x];
}
cout << ans+cnt/2;
return 0;
}
B.[CQOI2013] 二进制A+B,二进制
这种唐题竟然还有⚪。
比较简单的构造,猜了一个构造方式,但是在某种情况下(如下)不适用,挂成了 90 分。
除了上面说的这种情况我们都可以贪心的将位数比较多的那一个二进制串(设为 \(A\) 串)的一都放在后面,最后构造满足条件的最小的 \(B\) ,这样加和出来的 \(C\) 一定是最小的。
C.
没改。
11.12
最简单的一集,\(100+100+40=240\)
A.
开场十多分钟猜了个结论,感觉很对。
首先一个列肯定是连续一段处理,同一列把概率从小到大排个序处理即可。
考虑不同列的处理先后顺序,设 \(P_i\) 为第 \(i\) 列都死光的概率,\(E_i\) 为第 \(i\) 列的期望操作次数。
考虑相邻的两列 \(i,j\):
- 若 \(i\) 在 \(j\) 前面,那么我们的期望次数为 \(E_i+(1-P_i)E_j\)。
- 若 \(j\) 在 \(i\) 前面,那么我们的期望次数为 \(E_j+(1-P_j)E_i\)。
\(i\) 在 \(j\) 前当且仅当 \(E_i+(1-P_i)E_j\le E_j+(1-P_j)E_i\),移项得 \(\frac{P_i}{E_i}\ge\frac{P_j}{E_j}\)。
给列排完序后期望 dp 比较平凡。
B.
题解:
确认一个 \(d\) 是否合法是最大团问题,故难以直接解决。此类一般图上的 NPC 问题考虑加强限制转化为二分图问题求解。
枚举选出的点的直径 \((x_p,y_p),(x_q,y_q)\),那么其余选择的点都需要在以它们为圆心,\(d=\text{dis}(p,q)\) 为半径的圆内,两个圆会交出一个区域,连接 \(p,q\) 将这一区域划分为上下两部分,同一部分内的点距离均 \(\le d\),故排斥关系只会在两部分之间。
将排斥关系连边,求二分图最大独立集即可。
时间复杂度 \(O(n^5)\),常数极小,可以通过。
我不选择挑战 \(\text{NPC}\),于是二分答案之后贪心的把点按度数从小到大排个序,如果当前点加入能最大团且加入最大团后,在最大团内的点的出点的交 \(\ge m\),那么就加入。
显然这个东西没有正确性,所以 shuffle 它个 1777 次就能过了。
事实上根本不需要这么多次。
C.Furukawa Nagisa's Tree
又是经典的正难则反。
定义满足 \(\equiv x\pmod y\) 的为好路径,反之为坏路径
不合法的三元组一定有恰好两个点同时连接一个好路径和坏路径,设 \(in1_i,out1_i\) 分别为终点是 \(i\) 和起点是 \(i\) 的好路径个数,同理可得 \(in0_i,out0_i\),那么该点贡献为 $$2in1_iin0_i+2out1_iout0_i+in1_iout0_i+in0_iout1_i$$
由于每个三元环都会被统计两次,所以最后不合法情况数要除以二,最后答案为 \(n^3-\frac{ans}{2}\)。
\(in1_i\) 和 \(out1_i\) 可以点分治求,\(in0_i=n-in1_i,out0_i=n-in0_i\)。
鲜花
\(A\) 题写完后挂了个拍子发现寄掉了。
原因是排序的时候直接按成功概率从大往小拍,概率相同按期望次数从小到大拍。
bool cmp(int x,int y){return s[x] == s[y] ? b[x] < b[y] : s[x] > s[y];};
于是我观察到概率从大到小,期望从小到大,于是怒写了 4 个 cmp 取最小值
bool cmp1(int x,int y){return (s[x]/max(b[x],1e-8)) > (s[y]/max(b[y],1e-8));}
bool cmp2(int x,int y){return s[x] == s[y] ? b[x] < b[y] : s[x] > s[y];};
bool cmp3(int x,int y){return b[x] == b[y] ? s[x] > s[y] : b[x] < b[y];};
bool cmp4(int x,int y){return s[x]-b[x] > s[y]-b[y];}
赛后发现第一个就猜中了。