[NOIP 2023 模拟7]路程
[NOIP 2023 模拟7]路程
题意:
给一个数字n:
起点为1,终点为114
这张图正好有n+1条从1到114的路径,并且在这些路径的边权和中,[0,n]的每一个整数正好出现了一次。
问:给出一种可能的建图方法
----约束:边数<=45,点数<=18(包含1和114)
思路:
一. 考场做法:考虑将(n+1)分解质因数,(n+1)可能为素数或合数
1.若为素数:先将第n+1条边建起,然后考虑将(n)进行质因数分解
2.若为合数:则直接进行质因数分解
隐患:若分解后的质因数数集中有大于45的素数,则不符合题意
二. 转换方法:
n的范围是[1,32500],将32500进行二进制分解,位数不超过15位。
举例说明:
13(10) == 1101(2)
如图建边:(思路与数位dp相似)
[1] [2]---0/4--->[3]---0/2--->[4]---0/1--->[114]
| ^ ^ ^
|----0--->| | |
| | |
|---------8----------->| |
| |
|---------------------12/13---------------------->|
代码:
#include <bits/stdc++.h>
using namespace std;
int n,wei,cnt;
vector<pair<int,int> > ed[20];
void init() {
int nn=n;
while(nn) {
wei++;
nn>>=1;
}
}
int main() {
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
cin>>n;
init();
for(int i=2;i<=wei-1;i++) {
ed[i].push_back({i+1,0});
ed[i].push_back({i+1,(1<<(wei-i))});
cnt+=2;
}
ed[wei].push_back({114,0});
ed[wei].push_back({114,1});
cnt+=2;
int nn=n;
for(int i=0;i<wei;i++) {
if(!((nn>>i)&1)) continue;
int val=n-(((1<<(i+1))-1)&n);
if(!i) ed[1].push_back({114,val});
else ed[1].push_back({wei-i+1,val});
cnt++;
}
ed[1].push_back({114,n});
cnt++;
cout<<wei+1<<' '<<cnt<<'\n';
for(int i=1;i<=wei;i++) {
for(int j=0;j<(int)ed[i].size();j++) {
cout<<i<<' '<<ed[i][j].first<<' '<<ed[i][j].second<<'\n';
}
}
return 0;
}