Codeforces 1103 简要题解(持续更新)
传送门
又一场原地爆炸的比赛。
A题
传送门 简单思维题
题意:给一个4∗44*44∗4的格子图和一个01串,你要根据01串放1∗21*21∗2的木块,如果是0就竖放一个,是1就横放一个,一行或者一列满了可以直接消掉。
现在让你每次输出放下木块的坐标,并保证所有操作中没有木块相交。
思路:
可以直接按照自己的思路模拟然后我这个sb想了好久用什么策略
下来之后突然想到一个更简单的做法,我们保证如果竖放先放(1,1)(1,1)(1,1),如果横放先放(4,4)(4,4)(4,4),这样接下来无论是横放还是竖放都会消掉之前放过的一个。
比赛代码(丑:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
char s[1005];
int n,vis[5][5];
inline void update(){
for(ri i=1;i<=4;++i)if(vis[i][1]&&vis[i][2]&&vis[i][3]&&vis[i][4])vis[i][1]=vis[i][2]=vis[i][3]=vis[i][4]=0;
for(ri i=1;i<=4;++i)if(vis[1][i]&&vis[2][i]&&vis[3][i]&&vis[4][i])vis[1][i]=vis[2][i]=vis[3][i]=vis[4][i]=0;
}
inline void find(int type){
if(!type){
for(ri j=1;j<=4;++j)for(ri i=1;i<=3;++i){
if(!vis[i][j]&&!vis[i+1][j]){
vis[i][j]=vis[i+1][j]=1;
cout<<i<<' '<<j<<'\n';
return update();
}
}
}
else{
for(ri i=1;i<=4;++i)for(ri j=1;j<=3;++j){
if(!vis[i][j]&&!vis[i][j+1]){
vis[i][j]=vis[i][j+1]=1;
cout<<i<<' '<<j<<'\n';
return update();
}
}
}
}
int main(){
scanf("%s",s+1),n=strlen(s+1);
for(ri i=1;i<=n;++i)find(s[i]-'0');
return 0;
}
B题
传送门 二分答案
题意简述:你需要通过不超过60次询问来猜一个数a,a≤1e9a,a\le1e9a,a≤1e9,每次可以问两个数x,yx,yx,y,如果(xmod  a)≥(ymod  a)(x\mod a)\ge (y\mod a)(xmoda)≥(ymoda)会返回000否则返回111
思路:
我们考虑倍增求出aaa的范围然后再二分求aaa的准确值。
最开始先让x=0,y=1x=0,y=1x=0,y=1然后问,如果返回111就说明aaa不在区间(x,y](x,y](x,y]里,那么令x=y,y=y∗2+1x=y,y=y*2+1x=y,y=y∗2+1继续问下去知道返回000位置。
返回000时说明aaa的取值在(x,y](x,y](x,y]之间,那么我们二分aaa的值一直问x,midx,midx,mid来缩小范围即可。
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
char s[6];
typedef long long ll;
int main(){
while(scanf("%s",s)){
if(s[0]=='e')break;
if(s[0]=='m')break;
ll x=0,y=1;
while(cout<<"? "<<x<<' '<<y<<endl,fflush(stdout),scanf("%s",s),s[0]=='y'){
x=y,y=x*2+1;
}
if(s[0]=='e')break;
ll l=x+1,r=y,ans=y;
while(l<=r){
ll mid=l+r>>1;
bool f;
cout<<"? "<<x<<' '<<mid<<endl,fflush(stdout),scanf("%s",s),f=s[0]=='x';
if(s[0]=='e')break;
if(f)r=mid-1,ans=mid;
else l=mid+1;
}
cout<<"! "<<ans<<endl;
fflush(stdout);
}
return 0;
}
C题
传送门 结论+构造
题意简述:给一张无重边自环的无向图,每个点度数至少为3,现在给出kkk,要求你构造出以下两类答案中的任意一种:
- 找到一条路径,使得其长度≥nk\ge\frac nk≥kn
- 找到kkk个环,使得每个环长度至少为333且不为333的倍数,且每个环中至少有一个点在所有输出的环中只出现过一次。
思路:
我们先dfsdfsdfs构造出图的一个生成树,这个时候如果闲的胃疼可以求一遍直径去构造第一类答案,也可以直接枚举每个点的深度来判断能不能简单构造,如果不行就直接去构造多个环的答案。
然后考虑如何构造出多个环的答案,在生成树上面每个数的深度都不能满足条件的时候说明至少有kkk个 叶子结点,由于每个点度数至少为333,因此这个叶子结点除了连接父亲的树边以外还存在跟自己祖先连接的两条非树边 注意这里说的是祖先,因为如果不是祖先的话dfs的时候会走过去,它就不是叶子节点了
然后说明至少有两个环,且这个叶子结点可以当做那个在所有环中只出现过一次的特殊点。
然后可以用如下方法构造答案。
- 两个环中至少有一个长度不是333的倍数,直接输出这个环。
- 两个环的长度都是333的倍数,那么设离当前叶子结点近的点是yyy,原的是xxx,叶子结点是zzz,我们把y−>xy->xy−>x路径上所有点和zzz拿来构造成一个环,这个环的长度是3k+13k+13k+1满足题意。
代码(略丑:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=250005,M=5e5+5;
int n,m,k,dep[N],fa[N];
bool leaf[N],vis[N];
vector<int>e[N];
void dfs(int p){
leaf[p]=vis[p]=1;
for(ri i=0,v;i<e[p].size();++i){
if(vis[v=e[p][i]])continue;
leaf[p]=0,dep[v]=dep[p]+1,fa[v]=p,dfs(v);
}
}
int main(){
n=read(),m=read(),k=read();
for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
dep[1]=1,dfs(1);
int tmp=(n+k-1)/k;
for(ri i=1;i<=n;++i){
if(dep[i]>=tmp){
puts("PATH");
printf("%d\n",dep[i]);
while(i){
printf("%d ",i);
i=fa[i];
}
return 0;
}
}
puts("CYCLES");
vector<int>pos;
for(ri i=1;i<=n&&pos.size()<k;++i)if(leaf[i])pos.push_back(i);
for(ri i=1,p;i<=k;++i){
bool f=0;
p=pos[i-1];
for(ri j=0,v;j<e[p].size();++j){
if((v=e[p][j])!=fa[p]&&(dep[p]-dep[v]+1)%3){
printf("%d\n",dep[p]-dep[v]+1),f=1;
while(fa[p]!=fa[v]){
printf("%d ",p);
p=fa[p];
}
printf("%d",p);
puts("");
break;
}
}
if(f)continue;
vector<int>bad;
for(ri j=0,v;j<e[p].size()&&bad.size()<2;++j)if((v=e[p][j])!=fa[p])bad.push_back(v);
int x=bad[0],y=bad[1];
if(dep[x]>dep[y])swap(x,y);
printf("%d\n",dep[y]-dep[x]+2);
printf("%d ",p);
while(fa[x]!=fa[y]){
printf("%d ",y);
y=fa[y];
}
printf("%d",y);
puts("");
}
return 0;
}
D题
传送门 状压dp好题
题意简述:给nnn个二元组和一个kkk。
二元组(ai,ei)(a_i,e_i)(ai,ei)表示第iii个位置的权值是aia_iai,贡献是eie_iei。
现在对于每个位置可以让它的权值除以它自己一个不超过kkk的约数,要求从nnn个数中选择若干个数出来,使得它们的权值在除以约数过后的gcdgcdgcd为111,花费的代价是选出来的选择数的个数乘上选出来的所有数的贡献和。
数据范围:n≤1e5,k,ai≤1e12n\le1e5,k,a_i\le1e12n≤1e5,k,ai≤1e12
思路:
考虑到gcdgcdgcd的范围也是[1,1e12][1,1e12][1,1e12],不难证明这个gcdgcdgcd最多有不超过121212个不同的质因子。
先思考一个问题:怎么去除自己的约束最优?
我们先令gcd=a1p1a2p2...akpkgcd=a_1^{p_1}a_2{p^2}...a_k^{p_k}gcd=a1p1a2p2...akpk
显然对于一个要计入答案的数valvalval,只用关心它和gcdgcdgcd都有的质因数。
所以令val=t∗a1q1a2q2...akqkval=t*a_1^{q_1}a_2^{q_2}...a_k^{q_k}val=t∗a1q1a2q2...akqk,其中(t,a1)=(t,a2)=...=(t,ak)=1(t,a_1)=(t,a_2)=...=(t,a_k)=1(t,a1)=(t,a2)=...=(t,ak)=1
那么对于这个数,我们选择它肯定就要它自己使得若干个幂次不为000的质数幂全部去掉,否则不如不选它,即去掉a1q1,a2q2,...,akqka_1^{q_1},a_2^{q_2},...,a_k^{q_k}a1q1,a2q2,...,akqk中的至少一个 这里去掉的意思指的是用除法除掉,这样之后取gcd的时候如果选这个数这个被除掉的质数幂就不会算进去
这样一来,我们选出来的每个数至少会去掉一种之前没出现过的质数幂,因此最多选择不超过12个数就能够使得它们在除去质因子之后的gcdgcdgcd为1。
于是可以设计状态fi,jf_{i,j}fi,j表示现在已经去除的质因子的集合为iii,用了jjj个数。
考虑先把所有相同的数并在一起(因为它们分解之后也是相同的)
转移的话可以先对于每个数预处理出另外一个dpdpdp数组ggg,gig_igi表示对于这个数要使得被除去的质数幂状态为iii所需的最小这个数的个数(因为把相同的并在一起所以每个数的个数不一定是1)。
然后再枚举子集转移就行了。
细节较多。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline ll read(){
ll ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
inline map<ll,int> solve(ll x){
map<ll,int>ret;
ret.clear();
for(ll i=2;i*i<=x;++i)while(x%i==0)x/=i,++ret[i];
if(x^1)++ret[x];
return ret;
}
const ll inf=1e18;
const int N=(1<<12)+5,M=13,K=1e6+5;
int n,m;
ll k,a[K],b[K],gc,res=inf;
int main(){
n=read(),k=read(),gc=0;
for(ri i=1;i<=n;++i)gc=__gcd(a[i]=read(),gc);
for(ri i=1;i<=n;++i)b[i]=read();
map<ll,int>fac=solve(gc);
vector<ll>divv;
for(map<ll,int>::iterator it=fac.begin();it!=fac.end();++it)divv.push_back(it->first);
m=divv.size();
vector<ll>v(m);
map<vector<ll>,vector<ll> >mp;
for(ri i=1;i<=n;++i){
for(ri j=0;j<m;++j){
v[j]=1;
while(a[i]%divv[j]==0)a[i]/=divv[j],v[j]*=divv[j];
}
mp[v].push_back(b[i]);
}
vector<vector<ll> >f(1<<m,vector<ll>(m+1,inf));
f[0][0]=0;
for(map<vector<ll>,vector<ll> >::iterator it=mp.begin();it!=mp.end();++it){
vector<ll>mul=it->first,sum=it->second,g(1<<m,inf);
sort(sum.begin(),sum.end());
for(ri i=1;i<sum.size();++i)sum[i]+=sum[i-1];
for(ri i=0;i<(1<<m);++i){
ll mult=1;
for(ri j=0;j<m;++j)if((i>>j)&1)mult*=mul[j];
if(mult<=k)g[i]=1;
}
for(ri i=0;i<(1<<m);++i)for(ri j=i;j;j=(j-1)&i){if(j==i)continue;g[i]=min(g[i],g[i^j]+g[j]);}
for(ri i=(1<<m)-1;~i;--i)for(ri j=i;j;j=(j-1)&i){
if(g[j]>sum.size())continue;
for(ll k=0;g[j]+k<=m;++k)f[i][k+g[j]]=min(f[i][k+g[j]],f[i^j][k]+sum[g[j]-1]);
}
}
for(ri i=0;i<=m;++i)if(f[(1<<m)-1][i]^inf)res=min(res,f[(1<<m)-1][i]*i);
cout<<(res==inf?-1:res);
return 0;
}
Codeforces 1103 简要题解(持续更新)的更多相关文章
- Codeforces 863 简要题解
文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...
- LeetCode python实现题解(持续更新)
目录 LeetCode Python实现算法简介 0001 两数之和 0002 两数相加 0003 无重复字符的最长子串 0004 寻找两个有序数组的中位数 0005 最长回文子串 0006 Z字型变 ...
- Codeforces 1098 简要题解
文章目录 前言 A题 B题 C题 D题 E题 传送门 前言 没错因为蒟蒻太菜了这场的最后一道题也咕掉了,只有AAA至EEE的题解233 A题 传送门 题意简述:给出一棵带点权的树,根节点深度为111, ...
- Codeforces 381 简要题解
做的太糟糕了...第一题看成两人都取最优策略,写了个n^2的dp,还好pre-test良心(感觉TC和CF的pretest还是很靠谱的),让我反复过不去,仔细看题原来是取两边最大的啊!!!前30分钟就 ...
- [bzoj\lydsy\大视野在线测评]题解(持续更新)
目录: 一.DP 二.图论 1.最短路 2.强连通分量 三.利用单调性维护 四.贪心 五.数据结构 1.并查集 六.数学 1.计数问题 2.数学分析 七.博弈 八.搜索 /////////////// ...
- Codeforces 873 简要题解
文章目录 A题 B题 C题 D题 E题 F题 传送门 A题 传送门 题意: 一个人要做nnn件事,时间花费分别为a1,a2,...,an,a1≤a2≤a3≤...≤ana_1,a_2,...,a_n, ...
- Codeforces 1120 简要题解
文章目录 A题 B题 C题 D题 E题 F题 传送门 A题 传送门 题意简述:给你一个mmm个数的数列,现在规定把一个数列的1,2,...,k1,2,...,k1,2,...,k分成第一组,把k+1, ...
- Codeforces 1114 简要题解
文章目录 A题 B题 C题 D题 E题 F题 传送门 然而这场div2div2div2没有什么难度比较大的题 A题 传送门 题意简述:三个人分别至少选x,y,zx,y,zx,y,z件物品,有三种物品数 ...
- Codeforces 1110 简要题解
文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 众所周知ldxoildxoildxoi这种菜鸡选手是不会写HHH题的,因此该篇博客只有AAA题至GGG题的题解,实在抱歉. A题 传送门 题 ...
随机推荐
- Ionic后退刷新
版本:Angular 1.5.3.Ionic1.3.2 一 禁用缓存,全页面刷新. 每次前进/ 后退时,控制器都会执行. 1 AngularJS ui-router路由禁用缓存 var app = a ...
- 问题2:css图片、文字居中
1. 文本或图片水平对齐:父元素中添加以下样式 text-align : center;2. 单行文字垂直对齐:父元素中添加以下样式 line-height : 父元素高度; 3.图片 ...
- TZOJ 4085 Drainage Ditches(最大流)
描述 Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. Th ...
- python第三方库requests简单介绍
一.发送请求与传递参数 简单demo: import requests r = requests.get(url='http://www.itwhy.org') # 最基本的GET请求 print(r ...
- Q in Q
简介 Q in Q技术(也称Stacked VLAN 或Double VLAN).标准出自IEEE 802.1ad,将用户私网VLAN Tag封装在公网VLAN Tag中,使报文带着两层VLAN Ta ...
- ubuntu系列-安装jdk以及eclipse(for C++)
1.安装jdk eclipse是使用java语言开发的,一个java应用程序的运行要在java虚拟机下.在没有安装jdk的前提下,即使在ubuntu上安装了eclipse也不能使用. (1)首先在官网 ...
- java 基础之--java动态代理
1.抽象角色:声明真实对象与代理对象的共同接口: 2.代理角色:相当于中介的作用,bridge,内部包含对真实角色的reference,在执行真实操作对象时,附加其他操作,相当于对真实角色的封装: 3 ...
- php的ob缓存详解
前言引入 先看下面的代码: 这个代码,每次输出后都有sleep(1),表示程序执行暂定一秒,想象中浏览器应该是每隔1s钟,逐渐显示1到5的,然后事实情况确不是,浏览器访问的时候,等了5s种后,页面上一 ...
- 交叉编译libudev
一.交叉编译libudev下载udev-182.tar.xz 下载网址:https://mirrors.edge.kernel.org/pub/linux/utils/kernel/hotplug/ ...
- android显示通知栏Notification以及自定义Notification的View
遇到的最大的问题是监听不到用户清除通知栏的广播.所以是不能监听到的. 自定义通知栏的View,然后service运行时更改notification的信息. /** * Show a notificat ...