2024年7月西大大附中集训记录
7.11
UOJ354 新年的投票
提交答案题。
subtask1
做出来了。考虑你一个人根据当前局面的 0/1 数量哪个更多猜测自己头顶的数字。感觉非常没有道理但是相当优秀。
void solve(){
freopen("your_answer.out", "w", stdout);
int n = 15;
for(int i = 1; i <= n; i++){
for(int s = 0; s < (1ll << 14); s++){
int cnt1 = __builtin_popcount(s);
int cnt0 = 14 - cnt1;
int now = cnt1 > cnt0;
if(now) cnt1++;
cout<<cnt1%2;
}
endl;
}
}
subtask3
非常有启发性。讲题人不放分值导致我直接去想 subtask2 了/fn
构造如下:对于一个人 \(i\) 如果前面没有出现 0/1 就猜自己头上的数为 1 然后投 \(2^{k-1}\) 票,否则不投票。这样的构造会使得只有左边第一个 1 的票有效。显然只有全 0 的情况会错。
void solve(){
int n = 15;
freopen("your_answer3.out", "w", stdout);
for(int i = 1; i <= n; i++){
for(int s = 0; s < (1ll << 14); s++){
int now = 1;
bool fl = 1;
for(int j = 1; j < i; j++){
if(s & (now)){
fl = 0;
}
now <<= 1;
}
if(fl){
int cnt1 = __builtin_popcount(s);
if(cnt1 % 2 == 0) write((1ll << (i - 1)));
else write(-1 * (1ll << (i - 1)));
put();
}else{
write(0);
put();
}
}
endl;
}
}
subtask2
有了刚才的启发,这个显得简单了不少。
注意到 \(15 = 1 + 2 + 4 + 8\) 所以考虑把这些人分成人数为 \(2^i\) 的 \(4\) 组。然后把组内的数合想象为一个数进行 sub3 的操作。如果前面的组没有组总的异或和为 1 就猜自己这一组异或和为一然后投一票。否则我们可以通过组内有的投 \(1\) 有的投 \(0\) 把票数抵消掉。相当于投 \(0\) 票。
int bel[MAX], col[MAX];
int le[MAX];
void solve(){
int n = 15;
int now = 1, lft = 1;
for(int i = 1; i <= n; i++){
bel[i] = now;
lft--;
if(lft >= (1ll << (now - 1)) / 2){
le[i] = 1;
}else{
le[i] = 0;
}
// write(lft), endl;
if(!lft){
now++;
lft = (1ll << (now - 1));
}
}
// for(int i = 1; i <= n; i++){
// write(le[i]), put();
// }endl;
freopen("your_answer2.out", "w", stdout);
for(int i = 1; i <= n; i++){
for(int s = 0; s < (1ll << 14); s++){
int now = 1;
for(int j = 1; j <= 10; j++) col[j] = 0;
for(int j = 1; j < i; j++){
if(s & (now)){
col[bel[j]] ^= 1;
}
now <<= 1;
}
for(int j = i + 1; j <= n; j++){
if(s & now){
col[bel[j]] ^= 1;
}
now <<= 1;
}
bool fl = 1;
for(int j = 1; j < bel[i]; j++){
if(col[j]) fl = 0;
}
if(fl){
int now = 1;
for(int j = 1; j <= 10; j++){
if(j == bel[i]) continue;
now ^= col[j];
}
write(now);
}else{
write(le[i]);
}
}
endl;
}
}
subtask4
你不会认为我会吧,不会吧。