ZR 集训 Day18 - 人类智慧题合集
什么是人类智慧题?
为了方便通过关键词搜索到这篇文章,在此解释一下我对人类智慧题的定义:构造(交互、通信)、Ad-hoc、随机。
题单
CF1110E Magic Stones
牛炸了的 Ad-hoc。
首先特判 与 是否相等。
先看一组变化:
尝试差分,得到:
会发现以上操作对于差分数组而言就是交换相邻项,所以直接判断差分数组构成的集合是否相等即可。
代码:
C++
#include
using namespace std;
const int N=2e5+5;
int n;
int a[N],t[N];
multiset s1,s2;
int main(){
cin>>n;
for(int i=1;i>a[i];
if(i>1) s1.insert(a[i]-a[i-1]);
}
for(int i=1;i>t[i];
if(i>1) s2.insert(t[i]-t[i-1]);
}
if(a[1]==t[1]&&a[n]==t[n]&&s1==s2) cout
`#include
using namespace std;
const int N=2e5+5;
int n;
int a[N],t[N];
multisetint> s1,s2;
int main(){
cin>>n;
for(int i=1;in;i++){
cin>>a[i];
if(i>1) s1.insert(a[i]-a[i-1]);
}
for(int i=1;in;i++){
cin>>t[i];
if(i>1) s2.insert(t[i]-t[i-1]);
}
if(a[1]==t[1]&&a[n]==t[n]&&s1==s2) cout"Yes";
else cout"No";
return 0;
}`
CF1270G Subset with Zero Sum
看到这个特殊的数据范围:,考虑变化成:。
如果连边:,之后找到一个环 ,则有:
\begin{aligned} \sum_{i\in S} i&=\sum_{i\in S} next_i \\ \sum_{i\in S} i&=\sum_{i\in S} i-a_i \\ \sum_{i\in S} a_i &= 0 \end{aligned}
此时输出找到的环的下标即可。
代码:
C++
#include
using namespace std;
const int N=1e6+5;
int T,n,now,idx;
int a[N],nxt[N],tag[N],ans[N];
void solve(){
now=1,idx=0;
scanf("%d",&n);
for(int i=1;i
`#include
using namespace std;
const int N=1e6+5;
int T,n,now,idx;
int a[N],nxt[N],tag[N],ans[N];
void solve(){
now=1,idx=0;
scanf("%d",&n);
for(int i=1;in;i++){
scanf("%d",a+i);
nxt[i]=i-a[i],tag[i]=0;
}
while(!tag[now]) tag[now]=1,now=nxt[now];
ans[++idx]=now,now=nxt[now];
while(now!=ans[1]) ans[++idx]=now,now=nxt[now];
coutidx"\n";
for(int i=1;iidx;i++) coutans[i]" ";
cout"\n";
}
int main(){
scanf("%d",&T);
while(T--) solve();
return 0;
}`
CF1672E notepad.exe
观察限制,显然询问次数是 次的。
暴力的想法肯定是枚举一个高度,对于当前高度二分找到最小的宽度。
不难发现,当高度递增,为了使答案最优,我们肯定希望宽度递减。
这是我们先二分找出当高度为 时最小的宽度 ,
之后枚举高度 ,最优情况下可以省去 个空格,也就是最优的宽度应为 ,向上取整一下就是 。又因为想要更新答案,宽度至少为 ,发现此时宽度的取值是一定的,那就直接猜这个值,如果符合条件就更新答案。
代码:
C++
#include
using namespace std;
int n,x,w1,ans;
int main(){
cin>>n;
int l=1,r=4002000;
while(l>1;
cout>x;
if(x==1) r=mid;
else l=mid+1;
}
ans=w1=l;
for(int h=2;h>x;
if(x!=0) ans=min(ans,w1/h*x);
}
cout
`#include
using namespace std;
int n,x,w1,ans;
int main(){
cin>>n;
int l=1,r=4002000;
while(lr){
int mid=(l+r)>>1;
cout"? "midendl;
cin>>x;
if(x==1) r=mid;
else l=mid+1;
}
ans=w1=l;
for(int h=2;hn;h++){
cout"? "w1/hendl;
cin>>x;
if(x!=0) ans=min(ans,w1/h*x);
}
cout"! "ansendl;
return 0;
}`
HDOJ6664 Andy and Maze
随机算法。
直接做的话,要求 个点不能重复,这个状态很难记录,所以我们考虑随机染色,找颜色不同的路径。
这样答案路径被统计的概率就应该是 ,重复差不多 次正确率就非常高了。
代码:
C++
#include
using namespace std;
const int N=1e4+5;
int T,n,m,k;
int col[N],dp[N][64],u[N],v[N],w[N];
void solve(){
int ans=-1,num=300;
cin>>n>>m>>k;
for(int i=1;i>u[i]>>v[i]>>w[i];
while(num--){
memset(dp,-0x3f,sizeof dp);
for(int i=1;i>T;
while(T--) solve();
return 0;
}
`#include
using namespace std;
const int N=1e4+5;
int T,n,m,k;
int col[N],dp[N][64],u[N],v[N],w[N];
void solve(){
int ans=-1,num=300;
cin>>n>>m>>k;
for(int i=1;im;i++) cin>>u[i]>>v[i]>>w[i];
while(num--){
memset(dp,-0x3f,sizeof dp);
for(int i=1;in;i++) col[i]=rand()%k,dp[i][1col[i]]=0;
for(int s=1;s(1k);s++) for(int i=1;im;i++){
if(s&(1col[u[i]]))
dp[u[i]][s]=max(dp[u[i]][s],dp[v[i]][s^(1col[u[i]])]+w[i]);
if(s&(1col[v[i]]))
dp[v[i]][s]=max(dp[v[i]][s],dp[u[i]][s^(1col[v[i]])]+w[i]);
}
for(int i=1;in;i++) ans=max(ans,dp[i][(1k)-1]);
}
if(ans!=-1) coutans"\n";
else cout"impossible\n";
}
int main(){
cin>>T;
while(T--) solve();
return 0;
}`
CF1408F Two Different
不难发现,对于 是很好构造的,我们甚至可以做到让所有数字都变得一样。因为相邻两个是可以变成相同数字的,我们可以用类似归并的思想,递归直到只剩两个元素,否则对左右两边一一配对,就完成了合并。
其他情况呢?这里题目要求至多两种不同的数字,根据上面的分析,我们要把 拆成两个 相加的形式,但如果拆不了呢?可以想到,如果这两个 有重合的部分也是没关系的,因为后面的会把前面的覆盖掉,所以就做完了。
代码:
C++
#include
using namespace std;
int n,p,q,x;
vector> ans;
void solve(int id,int k){
if(k==0) return;
if(k==1){
ans.push_back({id+1,id+2});
return;
}
solve(id,k-1),solve(id+(1>n;
for(int i=0;i
`#include
using namespace std;
int n,p,q,x;
vectorpairint,int>> ans;
void solve(int id,int k){
if(k==0) return;
if(k==1){
ans.push_back({id+1,id+2});
return;
}
solve(id,k-1),solve(id+(1(k-1)),k-1);
for(int i=1;i(1(k-1));i++) ans.push_back({id+i,id+i+(1(k-1))});
}
signed main(){
cin>>n;
for(int i=0;i20;i++) if(n&(1i)) q=p,p=i;
if(q!=0) q++,x=n-(1p);
solve(0,q);
solve(x,p);
coutans.size()"\n";
for(auto[x,y]:ans) coutx" "y"\n";
return 0;
}`
QOJ141 8 染色
蒟蒻第一次做通信题,核心就是两个优化:
- 对于度数 $
代码:
C++
#include
using namespace std;
vector Alice(int n,int m,vector u,vector v,vector c){
vector x,deg(n,0);
for(int i=0;i=8) x.push_back(c[i]>>2),x.push_back((c[i]>>1)&1);
return x;
}
vector Bob(int n,int m,vector u,vector v,vector x){
int idx=0;
vector c(n,-1),tag(n,-1),deg(n,0),g[200005];
for(int i=0;i=8) c[i]=(x[idx] dfs=[&](int u){
for(int v:g[u]) if(c[u]==c[v]&&tag[v]==-1){
tag[v]=tag[u]^1;
dfs(v);
}
};
for(int i=0;i t(8);
for(int v:g[i]) if(c[v]!=-1) t[c[v]]=1;
for(int j=0;j
`#include
using namespace std;
vectorint> Alice(int n,int m,vectorint> u,vectorint> v,vectorint> c){
vectorint> x,deg(n,0);
for(int i=0;im;i++) deg[u[i]]++,deg[v[i]]++;
for(int i=0;in;i++) if(deg[i]>=8) x.push_back(c[i]>>2),x.push_back((c[i]>>1)&1);
return x;
}
vectorint> Bob(int n,int m,vectorint> u,vectorint> v,vectorint> x){
int idx=0;
vectorint> c(n,-1),tag(n,-1),deg(n,0),g[200005];
for(int i=0;im;i++) g[u[i]].push_back(v[i]),g[v[i]].push_back(u[i]),deg[u[i]]++,deg[v[i]]++;
for(int i=0;in;i++) if(deg[i]>=8) c[i]=(x[idx]1)+x[idx+1],idx+=2;
functionvoid(int)> dfs=[&](int u){
for(int v:g[u]) if(c[u]==c[v]&&tag[v]==-1){
tag[v]=tag[u]^1;
dfs(v);
}
};
for(int i=0;in;i++) if(tag[i]==-1&&c[i]!=-1) tag[i]=0,dfs(i);
for(int i=0;in;i++) if(tag[i]!=-1) c[i]=(c[i]1)+tag[i];
for(int i=0;in;i++){
if(c[i]!=-1) continue;
vectorbool> t(8);
for(int v:g[i]) if(c[v]!=-1) t[c[v]]=1;
for(int j=0;j8;j++) if(!t[j]) c[i]=j;
}
return c;
}`