Educational Codeforces Round 157 (Rated for Div. 2) - VP 记录
Preface
啊啊啊为什么我老是被简单题卡啊!
A. Treasure Chest
A 题题面这么长吓我一跳。
分类讨论,钥匙在前面可以拿了钥匙直接到箱子那里;箱子在前面就尽量把箱子往钥匙搬,让折回的距离尽量小。
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int T; scanf("%d",&T);
while(T--)
{
int x,y,k; scanf("%d%d%d",&x,&y,&k);
if(y<=x) printf("%d\n",x);
else
{
x=min(x+k,y);
printf("%d\n",y+(y-x));
}
}
return 0;
}
B. Points and Minimum Distance
所求为曼哈顿距离,途中肯定不能走回头路(所以需要排序),所以距离只与起点和终点的坐标有关。
把点位排个序然后以前一半为横坐标,后一半为纵坐标,这样可以让起点和终点的曼哈顿距离最小且不走回头路。
证明不来,打出来发现样例对了我就直接交了,详细证明可以在这里找找,有好几篇有详细证明的。
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=105;
int n,a[N<<1];
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n<<1;i++)
scanf("%d",&a[i]);
sort(a+1,a+(n<<1)+1);
printf("%d\n",(a[n]-a[1])+(a[n<<1]-a[n+1]));
for(int i=1;i<=n;i++)
printf("%d %d\n",a[i],a[n+i]);
}
return 0;
}
C. Torn Lucky Ticket
不知道是什么做法的题。
因为位数较小,所以可以对于每个数都枚举位数并查找和。
具体来说,设 \(c(i,j)\) 表示长度为 \(i\),和为 \(j\) 的数的数量。
对于某个数 \(x\),设长度为 \(len_x\),\(sum_x(i)\) 表示右 \(i\) 个数位的和。
如果 \(x\) 要和在左侧拼上一个 \(y\) ,设 \(x\) 的右边 \(i\) 位要作为新数的右边部分,那么 \(x\) 左边还剩下 \((len_x-i)\) 位,新数左右两侧数位数量相等,所以 \(len_y+(len_x-i)=i\),解得 \(len_y=i-(len_x-i)\)。
此时右半部分的和为 \(sum_x(i)\),左侧剩下 \(sum_x(len_x)-sum_x(i)\),所需 \(y\) 的数位总和就为 \(sum_x(i)-(sum_x(len_x)-sum_x(i))\)。
所以此时合法的 \(y\) 的数量就为:
同理,要在 \(x\) 右边拼上一个 \(y\),合法的 \(y\) 的数量就为:
所以答案就为:
化简一下(\(sum_x\) 表示 \(sum_x(len_x)\)):
#include<cstdio>
using namespace std;
const int N=2e5+5;
int n,a[N];
int cnt[15][105];
int num[N][10],sum[N][10],len[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
int t=a[i];
while(t)
{
num[i][++len[i]]=t%10; t/=10;
sum[i][len[i]]=sum[i][len[i]-1]+num[i][len[i]];
}
cnt[len[i]][sum[i][len[i]]]++;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=len[i];j++)
{
//sum[j]=?+(sum[len]-sum[j]); j=?+(len-j)
int nd_len=j-(len[i]-j),nd_sum=sum[i][j]-(sum[i][len[i]]-sum[i][j]);
if(nd_len>0&&nd_sum>0) ans+=cnt[nd_len][nd_sum];
}
for(int j=1;j<=len[i];j++)
{
//sum[j]+?=sum[len]-sum[j]; j+?=len-j
int nd_len=(len[i]-j)-j,nd_sum=(sum[i][len[i]]-sum[i][j])-sum[i][j];
if(nd_len>0&&nd_sum>0) ans+=cnt[nd_len][nd_sum];
}
}
printf("%lld\n",ans);
return 0;
}
D. XOR Construction
好题。
如果知道了 \(b_1\) 其它的 \(b\) 也都能一下推出来(\(b_i=b_{i-1} \oplus a_{i-1}\))。
枚举 \(b_1\),通过二进制建立 \(a\) 前缀异或和的 01-Trie,插入 \(b_1\) 查找最大异或值,判断是否小于 \(n\) 即可。
这里有很多我这种做法的详解。
#include<cstdio>
#include<bitset>
using namespace std;
const int N=2e5+5,LogN=20;
int n,a[N],sum[N],b[N];
int trie[N<<1][2],idx=1;
void insert(bitset<LogN+5> x)
{
int p=1;
for(int i=LogN;i>=0;i--)
{
bool t=x[i];
if(!trie[p][t]) trie[p][t]=++idx;
p=trie[p][t];
}
return;
}
bool check(bitset<LogN+5> x)
{
int p=1,now=0;
for(int i=LogN;i>=0;i--)
{
bool t=x[i];
if(trie[p][!t]) //可以异或出1
{
p=trie[p][!t];
now=(now<<1)+1;
}
else
{
p=trie[p][t];
now=(now<<1)+0;
}
}
return now<n;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]^a[i];
insert(sum[i]);
}
for(b[1]=0;b[1]<n;b[1]++)
if(check(b[1])) break;
printf("%d ",b[1]);
for(int i=2;i<=n;i++)
printf("%d ",sum[i-1]^b[1]);
return 0;
}
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18543344