Week2 题解
T2
首先,异或这个位运算有个很好的性质:x^y^y=x
于是,可以有两种解决方法:
法一
既然让猜原来的数是多少,当它异或了一个其他值val的时候,再异或val就会变回原来的值。
于是对于第一次输入的数y,让其对输出100个数异或,产生的结果中肯定有一个是原来的数;再对第二次的100个数重复上述操作,同时出现在两次结果中的数即是原来的x
但是会有一个问题:
第一次没用的结果中会出现:\(a_i\) ^\(a_j\) ^\(x\)
第二次没用的结果中会出现:\(a_l\) ^ \(a_r\) ^\(x\)
如果两个数恰好相等就会出现尴尬情形
于是,选数的时候就需要讲究点儿了——要保证第一组任两个数异或的值与第二组任两个数异或的结果不相同
这样的数的一种构造方法是:第一组让前七位都是0,则异或结果前七位都是1,而后七位由于数不相同不可能都是1;第二组让后七位都是0,则异或结果后七位都是1.
满足构造的一种情形是第一组输出1,2,3……100;第二组输出这些数左移7位的结果
法二
如果让第一组的前七位都是0,那么可以通过异或出的结果推知原来数前七位的情况;第二组后七位都是0,可以通过结果推知原来数后七位的情况,再拼接在一起即可
T3
CF1451
根据异或的性质,如果知道了第一个数的值,再异或剩余的数就可以恢复序列了,所以首先用第一个数异或剩下的N-1个数花费n-1次询问是固定的
easy version
嗯……比较富裕,可以花三次询问求出第一个数
先来介绍两个恒等式
\(a\&b+a\)^\(b=a|b\)
\(a\&b+a|b=a+b\)
于是,在知道两数与与两数异或后可以求出两数和,可以花三次询问前三个数两两与的值,从而列三元方程可以求出a的值
hard version
嗯……这回有点儿穷,得省着点儿用了
发现题中还有两个条件没有被用到——\(0 \le a_i\le n-1\);n为2的整数幂
那么可以分两种情况讨论:
若序列中两两数各不相同,体现为异或值各不相同且非零,那么异或的值中必然有一个数是1,意味着除最后一位都是与\(a_1\)是一样的;还有一个数必然是n-2,意味着除最后一位都是与\(a_1\)是不同的。与这两个数与再拼合即可
若序列中有重复的数,再分情况讨论:
其中有一个数与 \(a_1\)相同,即异或值为零,直接求即可
另两个数相等,即异或值相等,这样让它俩与求出值再求\(a_1\)的值即可
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
int n,b[maxn],vis[maxn],ans,a[maxn],x,y;
bool flag;
int main(){
cin>>n;
for(int i=2;i<=n;i++){
cout<<"XOR "<<1<<" "<<i<<endl;
fflush(stdout);
cin>>b[i];
if(b[i]==0&&(!flag)){
flag=true;
cout<<"AND "<<1<<" "<<i<<endl;
fflush(stdout);
cin>>a[1];
}
if(vis[b[i]]&&(!flag)){
flag=true;
cout<<"AND "<<vis[b[i]]<<" "<<i<<endl;
fflush(stdout);
cin>>ans;
a[1]=ans^b[i];
}
else vis[b[i]]=i;
}
if(!flag){
cout<<"AND "<<1<<" "<<vis[n-2]<<endl;
fflush(stdout);
cin>>x;
cout<<"AND "<<1<<" "<<vis[1]<<endl;
fflush(stdout);
cin>>y;
a[1]=x|y;
}
cout<<"! "<<a[1];
for(int i=2;i<=n;i++){
cout<<" "<<(b[i]^a[1]);
}
cout<<endl;
fflush(stdout);
return 0;
}
T4
这道题主要的难点就在于对角线的值为零,也就是说每次询问的时候尽量避免问到对线上的值,或者说如果对于一行i如果存在\(w_j=i\)那么这次询问对于这行是没有用的。
反过来的看,要让每一行的每一个数都有对这一行最小值贡献的机会。
由于每一个不在对角线上的位置的列号和行号不相同,即写成二进制位后至少有一位与行号的不同
那么可以枚举二进制的每一位,分别询问这一位是0和1的行们即可~
代码
#include<bits/stdc++.h>
using namespace std;
int n,cnt,ans[1005],x;
int main(){
cin>>n;
memset(ans,0x3f,sizeof ans);
for(int i=0;i<=9;i++){
cnt=0;
for(int j=1;j<=n;j++){
if(j&(1<<i))cnt++;
}
if(cnt){
cout<<cnt<<endl;
for(int j=1;j<=n;j++){
if(j&(1<<i))cout<<j<<" ";
}
cout<<endl;
fflush(stdout);
for(int j=1;j<=n;j++){
cin>>x;
if(j&(1<<i))continue;
ans[j]=min(ans[j],x);
}
}
cnt=0;
for(int j=1;j<=n;j++){
if(!(j&(1<<i)))cnt++;
}
if(cnt){
cout<<cnt<<endl;
for(int j=1;j<=n;j++){
if(!(j&(1<<i)))cout<<j<<" ";
}
cout<<endl;
fflush(stdout);
for(int j=1;j<=n;j++){
cin>>x;
if(!(j&(1<<i)))continue;
ans[j]=min(ans[j],x);
}
}
}
puts("-1");
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
fflush(stdout);
return 0;
}
T5
首先,放置监听器数量的严格固定,相当于背包容量
这题有一个非常特殊的限制——“放置在节点 u 的监听设备并不监听 u 本身的通信”,于是对于一个点的状态并不只受当前点放不放的控制
另一个关键点是因为管辖半径为1,那么对于节点 u 来说,如果儿子没被监听,u也不监听,那么以后就没有机会了,所以对于 u 节点的子树,除 u 节点外其他节点必须已经被监听
于是设计状态: \(f[u][j][0/1][0/1]\)表示节点 u 的子树共使用 j 个监听器,u 节点是否放置, u 节点是否被监听,且此时其他点都被监听的方案数
由于涉及组合数学,可以考虑直接按照优化后的树形 dp 来写会更方便。
于是 4 种情况分别转移:
\(f[u][j+k][0][0]+=f[u][j][0][0]*f[v][k][0][1]\)
\(f[u][j+k][0][1]+=f[u][j][0][0]*f[v][k][1][1]+f[u][j][0][1]*(f[v][k][0][1]+f[v][k][1][1])\)
\(f[u][j+k][1][0]+=f[u][j][1][0]*(f[v][k][0][1]+f[v][k][0][0])\)
\(f[u][j+k][1][1]+=f[u][j][1][0]*(f[v][k][1][0]+f[v][k][1][1])+f[u][j][1][1]*(f[v][k][0][0]+f[v][k][0][1]+f[v][k][1][0]+f[v][k][1][1])\)
注意这道题 m 很小,而 \(size[u]\) 很大,需要取 min
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5,maxm=2e5+5;
const int mod=1e9+7;
int f[maxn][105][2][2],g[maxn][105][2][2],hd[maxn],cnt,m,n,siz[maxn],x,y;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct Edge{
int nxt,to;
}edge[maxm];
void add(int u,int v){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
hd[u]=cnt;
return ;
}
void dfs(int u,int father){
// cout<<u<<" ";
siz[u]=1;
f[u][0][0][0]=f[u][1][1][0]=1;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==father)continue;
dfs(v,u);
for(int j=0;j<=min(m,siz[u]);j++){
g[u][j][0][0]=f[u][j][0][0],f[u][j][0][0]=0;
g[u][j][0][1]=f[u][j][0][1],f[u][j][0][1]=0;
g[u][j][1][0]=f[u][j][1][0],f[u][j][1][0]=0;
g[u][j][1][1]=f[u][j][1][1],f[u][j][1][1]=0;
// cout<<g[u][j][0][0]<<" "<<g[u][j][0][1]<<endl;
}
for(int j=0;j<=min(m,siz[u]);j++){
for(int k=0;k<=min(m-j,siz[v]);k++){
f[u][j+k][0][0]=(f[u][j+k][0][0]+1ll*g[u][j][0][0]*f[v][k][0][1]%mod)%mod;
f[u][j+k][1][0]=(f[u][j+k][1][0]+1ll*g[u][j][1][0]*((f[v][k][0][0]+f[v][k][0][1])%mod)%mod)%mod;
f[u][j+k][0][1]=(f[u][j+k][0][1]+(1ll*g[u][j][0][1]*((f[v][k][0][1]+f[v][k][1][1])%mod)%mod+1ll*g[u][j][0][0]*f[v][k][1][1]%mod)%mod)%mod;
f[u][j+k][1][1]=(f[u][j+k][1][1]+(1ll*g[u][j][1][0]*((f[v][k][1][0]+f[v][k][1][1])%mod)%mod+1ll*g[u][j][1][1]*((((f[v][k][0][0]+f[v][k][0][1])%mod+f[v][k][1][0])%mod+f[v][k][1][1])%mod)%mod)%mod)%mod;
}
}
siz[u]+=siz[v];
}
return ;
}
int main(){
n=read();
m=read();
for(int i=1;i<=n-1;i++){
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(1,0);
cout<<(f[1][m][1][1]+f[1][m][0][1])%mod;
return 0;
}
CF1479B2 Painting the Array II
这道题是个贪心
首先先去重是肯定的,接着就会遇到这样一个问题:
这样的情况下很明显5和3只能合并一个,贪心需要考虑这两个当中的抉择
最后的规则是这样的,哪个数下一次出现的位置近就优先合并哪个数
可以感性理解一下,远的和近的的贡献都是1,而合并了近的一个会为后面的数创造更多可能
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],f[maxn],ans,last[maxn],nxt[maxn],sta1[maxn],tp1,sta2[maxn],tp2;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
last[i]=n+1;
}
n=unique(a+1,a+n+1)-a-1;
for(int i=n;i>=1;i--){
nxt[i]=last[a[i]];
last[a[i]]=i;
// cout<<nxt[i]<<" ";
}
// cout<<endl;
nxt[0]=n+1;
sta1[++tp1]=1;
ans++;
for(int i=2;i<=n;i++){
if(a[sta1[tp1]]==a[i])sta1[++tp1]=i;
else if(a[sta2[tp2]]==a[i]) sta2[++tp2]=i;
else{
if(nxt[sta1[tp1]]-i<nxt[sta2[tp2]]-i){
sta2[++tp2]=i;
}
else sta1[++tp1]=i;
ans++;
}
// for(int j=1;j<=tp1;j++)cout<<sta1[j]<<" ";
// cout<<endl;
// for(int j=1;j<=tp2;j++)cout<<sta2[j]<<" ";
// cout<<endl;
}
cout<<ans;
return 0;
}
CF1485D Multiples and Power Differences
构造神题
直接说做法吧,由于1~16所有可能值的乘积(这里比如2、4、8就可以被8代替)是720720 \(<\) 1000000
于是可以把对角线都填上720720,剩下的填 \(720720-x^4\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
int n,m,a[maxn][maxn];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
if((i&1)==(j&1)){
cout<<720720<<" ";
}
else{
cout<<720720-pow(a[i][j],4)<<" ";
}
}
cout<<endl;
}
return 0;
}
又一道神奇的构造题
这道题里面难受的地方就在于加减的是 \(x*i\),自由性很小
这时看到了1这个数字,要是都由1来发出,那么加数就可以是任意的了
但是如果原来比平均数大的数怎么办?这些数是需要将多的还给1的,但是由于特殊的限制需要先从1给那些数填成倍数,再全还给1,再从1给过去,这样一来正好 \(3*(n-1)\) 次
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int t,n,a[maxn],tp,sum;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct Node{
int x,y,z;
Node(){}
Node(int a,int b,int c):x(a),y(b),z(c){}
}sta[maxn*3];
int main(){
t=read();
while(t--){
n=read();
sum=0;
for(int i=1;i<=n;i++){
a[i]=read();
sum+=a[i];
}
if(sum%n){
puts("-1");
continue;
}
tp=0;
for(int i=2;i<=n;i++){
a[1]+=a[i];
if(a[i]%i){
sta[++tp]=Node(1,i,i-a[i]%i);
a[i]+=i-a[i]%i;
}
sta[++tp]=Node(i,1,a[i]/i);
a[i]=0;
}
int ev=sum/n;
for(int i=2;i<=n;i++){
sta[++tp]=Node(1,i,ev);
}
cout<<tp<<endl;
for(int i=1;i<=tp;i++){
printf("%d %d %d\n",sta[i].x,sta[i].y,sta[i].z);
}
}
return 0;
}
Week2 题解的更多相关文章
- ZROI week2
\[ZROI week2\] 除草机 首先考虑最少的拐点肯定是那种螺旋形状的,然后手玩几个数据发现和列数(行数)有关,且每增加1就是上一个状态加2,直接\(O(1)\)公式即可 吐槽:为啥\(n,m\ ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
随机推荐
- python编写DDoS攻击脚本
python编写DDoS攻击脚本 一.什么是DDoS攻击 DDoS攻击就是分布式的拒绝服务攻击,DDoS攻击手段是在传统的DoS攻击基础之上产生的一类攻击方式.单一的DoS攻击一般是采用一对一方式的, ...
- 第1篇-关于JVM运行时,开篇说的简单些
开讲Java运行时,这一篇讲一些简单的内容.我们写的主类中的main()方法是如何被Java虚拟机调用到的?在Java类中的一些方法会被由C/C++编写的HotSpot虚拟机的C/C++函数调用,不过 ...
- HDFS中NameNode工作机制
引言 NameNode: 存储元数据 管理整个HDFS集群 DataNode: 存储数据的block SecondaryNameNode: 辅助HDFS完成一些事情 NameNode和Secondar ...
- nat转换技术,且用且珍惜
一.NAT转换技术 1.1.NAT技术概述 随着Internet的发展和网络应用的增多,IPv4地址枯竭已经成为制约网络发展的瓶颈.尽管IPv6可以从根本上解决IPv4地址空间不足的问题,但目前众多的 ...
- JVM-超全图
- kali2020更换JDK&&安装burpsuite pro
写在前面 之前因为安装maven把JDK换成了1.8.0_261,尝试诸多方法依然打不开自带的burp,正好在做CTF做不出来 QAQ,一气之下打算安个破解版burp. 安装 0x00 更换JDK 使 ...
- 007 GMII、SGMII和SerDes的区别和联系
一.GMII和SGMII的区别和联系 GMII和SGMII区别,上一篇已经介绍了,这一篇重点介绍SGMII和SerDes区别. GMII和SGMII GMII 在MII接口基础上提升了数据位宽和Clo ...
- 将JDK默认编码设置为UTF-8
此博文非原创:参考小兵qwer https://blog.csdn.net/xiaobing_122613/article/details/70209716 只是想留下对自己有用的东西,同时帮助更 ...
- linux 的删除
1,删除 命令行 rm -rf 文件夹名称 2,下载 wget 网址 -------------------- 查找ES进程号 ps -ef | grep elastic kill -9 3250 3 ...
- GAC
GAC是什么?是用来干嘛的?GAC的全称叫做全局程序集缓存,通俗的理解就是存放各种.net平台下面需要使用的dll的地方.GAC的具体目录在windows/ assembly. 喜欢使用破解软件的朋友 ...