UOJ354 新年的投票
本文主要关注第四个子任务,前面三个有叙述不清的敬请谅解。
UOJ354 新年的投票
Sub1#
不管人的编号直接爆搜就能够找到一个合法方案。
Sub3#
第
这样子只会有最低位的
Sub2#
把
错误的情况只有四组内部的答案均为
Sub4#
考虑一个人的视角用向量表示,看到的
如果我们有一个关于上述向量的函数
刚刚那句话很抽象,我们详细解释一下。
首先明确
这里
假设
注意只能让一个人投,多个人都投会导致重复,此时如果想要让最后的答案式子的系数依旧是整数就很容易超过
这之后我们再考虑
所以我们尝试找到一个关于
这个多项式的目标是在尽可能多的地方得到正确的正负性,因此需要找到适合的零点拐弯。
经过一些计算你可以得到最优的情况是在
此时错误的数量可以计算,为
写出
乘上
可以看到确实控制在
接下来考虑怎么把这个式子分给每个人投票,即把
回顾定义,
将其代入,得到的也是一个
系数并不会炸,最大也就是对应次幂的系数乘上对应次数的阶乘,目测一下就知道活得好好的。
这东西你就慢慢折磨去吧,总是能搞出来的,毕竟提答题不用考虑时间复杂度,写个爆搜也是可以的。
具体实现不讲留给读者思考,也可以参考示例代码。
Code#
#include<bits/stdc++.h>
using namespace std;
#define __FILE_MODE__
namespace Task_1
{
//懒得写了,直接看 Task2。
//爆搜的思路就是根据看到的 $0$ 和 $1$ 的个数做出决策。
//可以搜出 6906 种错误情况的策略,似乎还有更优一点的但不重要。
}
namespace Task_2
{
constexpr int N=15;
int ans[N][1<<N];
inline int group(int x)//判断 x 是哪个组的。
{
if(x<1)return 1;
if(x<3)return 2;
if(x<7)return 3;
if(x<15)return 4;
return -1;
}
inline void work(int n)
{
#ifdef __FILE_MODE__
freopen("vote2.ans","w",stdout);
#endif
if((n+1)&n){printf("Invalid!");return;}//此策略仅对 $n=2^k-1$ 有效。
for(int S=0;S<(1<<(n-1));S++)
{
for(int i=0;i<n;i++)
{
bool fl=true;//判断前面是否全 0 的旗子。
int gr=group(i);
for(int j=1;j<gr;j++)
{
int sum=0;
for(int k=0;k<(1<<j)-1;k++)
{
if(group(k)!=j)continue;
sum^=(S>>k)&1;
}
if(sum!=0)fl=false;
}
if(fl)
{
int res=0;
for(int j=gr+1;j<=4;j++)
{
int sum=0;
for(int k=(1<<(j-1))-1;k<(1<<j)-1;k++)sum^=(S>>(k-1))&1;
res^=sum;
}
ans[i][S]=res^1;
}
else ans[i][S]=i&1;
}
}
for(int i=0;i<n;i++)
{
for(int S=0;S<(1<<(n-1));S++)printf("%d",ans[i][S]);
putchar(10);
}
return;
}
}
namespace Task_3
{
constexpr int N=15;
int ans[N][1<<N];
inline void work(int n)
{
#ifdef __FILE_MODE__
freopen("vote3.ans","w",stdout);
#endif
for(int S=0;S<(1<<(n-1));S++)
{
for(int i=0;i<n;i++)
{
bool fl=true;//判断前面是否全 0 的旗子。
for(int j=0;j<i;j++)if((S>>j)&1)fl=false;
if(fl)
{
int res=0;
for(int j=i;j<n-1;j++)res^=(S>>j)&1;
if(res)ans[i][S]=-(1<<i);else ans[i][S]=(1<<i);
}
else ans[i][S]=0;
}
}
for(int i=0;i<n;i++)
{
for(int S=0;S<(1<<(n-1));S++)printf("%d ",ans[i][S]);
putchar(10);
}
return;
}
}
namespace Task_4
{
constexpr int N=12,K=7;
constexpr int p[K+1]={43648605,-52371534,26389636,-7246232,1172080,-111776,5824,-128};
constexpr int NUM=792;//共有 C(12,7)=792 人。
int ans[NUM][1<<K];
int sight[NUM],rev[1<<N];//每个人看到的实际编号。
int bel[1<<N];//预处理出出现该情况时给谁投。
inline int func(int sight,int fact)//处理出这个人的实际所见。
{
int ret=0,tot=0;
for(int i=0;i<N;i++)if((sight>>i)&1)ret|=((fact>>i)&1)<<tot,tot++;
return ret;
}
inline int ppc(int V)
{
int s=0;
for(int i=0;i<N;i++)s+=(V>>i)&1;
return s;
}
void DFS(int now,int pw)
{
if(pw>K)return;//次数过大。
int id=bel[now];
int unchanged=func(sight[id],now);
for(int S=0;S<(1<<K);S++)if((S&unchanged)==unchanged)ans[id][S]+=p[pw];
for(int i=0;i<N;i++)DFS(now|(1<<i),pw+1);
return;
}
inline void work(int n,int k)
{
#ifdef __FILE_MODE__
freopen("vote4.ans","w",stdout);
#endif
if(n!=12||k!=7)return;//此策略仅对 $n=12,k=7$ 有效。
int tot=0;
for(int V=0;V<(1<<n);V++)
{
int s=ppc(V);
if(s==k)
{
sight[tot]=V;
rev[V]=tot;
tot++;
}
else rev[V]=-1;
}
for(int V=0;V<(1<<n);V++)
{
int s=ppc(V);
if(s>k)continue;
for(int i=0;i<NUM;i++){if((sight[i]&V)==V){bel[V]=i;break;}}
}
DFS(0,0);
for(int i=0;i<NUM;i++)
{
for(int S=0;S<(1<<k);S++)printf("%d ",ans[i][S]);
putchar(10);
}
return;
}
}
int main()
{
Task_2::work(15);
Task_3::work(15);
Task_4::work(12,7);
return 0;
}
完整文件有 1197.5kb 就不贴了,上面那个程序可以直接运行出来。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix