2025 暑假集训 Day13
2025.8.18
比赛,预期 \(30+100+100+30=260\),实际 \(27+24+0+10\) 排名 \(28/40\) 太抽象了。T1 挂分的原因居然是数据范围的锅,数据范围写的是 \(30 \%\) 的数据 \(100,100\%\) 的数据 \(10^4\),但是实际上用 assert 试了一下,只有前三个点的数据是 \(\le 10000\) 的,其他的都大于 \(10000\),我数组大小只开了 \(10007\) 然后就爆炸了,喜提 RE27pts.
B. 灯
洛谷原题:P2962 [USACO09NOV] Lights G
首先可以发现这个开关灯的操作就相当于给灯的状态异或上 \(1\),然后就可以发现每个灯最多操作 \(1\) 次,所以我们不妨考虑枚举每一个灯开不开,时间复杂度 \(O(2^n)\) 显然会爆炸。
考虑使用 meet-in-the-middle 算法,将原来的枚举开关灯分成两半 \(1 \sim mid,mid+1 \sim n\),其中 \(mid=\left\lfloor \dfrac{(1+n)}{2} \right\rfloor\)。每一个灯的开关都可以用 1/0 表示,一共有 \(35\) 个灯,所以可以用一个 long long state 存下,其中 \(state\) 从低到高第 \(i\) 位为 \(1\) 表示第 \(i\) 个灯是开着的,否则是关着的。
对于前半部分,分别枚举第 \(i(1 \le i \le mid)\) 个灯要不要操作,然后搜索出“到达状态 \(state\) 需要的最少操作次数 \(dp_{state}\)”,对于后半部分首先分别枚举第 \(i(mid+1 \le i \le n)\) 要不要操作,得出一个状态 \(state\),然后找到与当前状态互补的状态(就是两个状态的按位异或 \(\operatorname {xor}\) 正好为所有灯都开着的状态 \(2^n-1\)),统计 \(dp_{(2^n-1) \operatorname {xor} state}\) 与后半部分所用操作数的次数的和,然后取一个最小值即为答案。
因为折半搜索之后只要记录操作第 \(1 \sim mid\) 个灯的状态,一共 \(2^{mid}\) 种,可以用一个 map 存下。还有要注意的是 1<< 要写成 1ll<<,因为会爆 int。
#include<bits/stdc++.h>
#define popcnt(x) __builtin_popcount(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=40;
int n,m,ans=12180211;
vector<int> g[N];
map<ll,int> dp; //dp[state]表示到达状态state需要的最少操作步骤
int main()
{
// freopen("neuvillette.in","r",stdin);
// freopen("neuvillette.out","w",stdout);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++)
{
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
int mid=(1+n)>>1;
//搜索1~mid的部分
for(ll i=0;i<(1ll<<mid);i++) //i二进制第j位为1表示要操作灯j
{
ll state=0;
for(int j=1;j<=mid;j++)
{
if(i&(1ll<<(j-1)))
{
state^=1ll<<(j-1);
for(int v:g[j]) state^=1ll<<(v-1);
}
}
if(dp.find(state)==dp.end()) dp[state]=popcnt(i);
else dp[state]=min(dp[state],popcnt(i));
}
//搜索mid+1~n的部分
for(ll i=0;i<(1ll<<(n-mid));i++)
{
ll state=0;
for(int j=1,jj=mid+1;j<=n-mid;j++,jj++)
{
if(i&(1ll<<(j-1)))
{
state^=1ll<<(jj-1);
for(int v:g[jj]) state^=1ll<<(v-1);
}
}
//寻找互补状态
if(dp.find(((1ll<<n)-1)^state)!=dp.end()) ans=min(ans,popcnt(i)+dp[((1ll<<n)-1)^state]);
}
cout<<ans;
return 0;
}
/*
状态:state的第idx位为1表示这个灯是开着的,否则是关着的
987654321 idx
101010001 state
*/

浙公网安备 33010602011771号