T1T_1T1——num(2032)
Description:
求第nnn个刚好有kkk个111的二进制正整数。
n≤1030,2≤k≤20n\le10^{30},2\le k\le20n≤1030,2≤k≤20,答案长度不超过100010001000
Solution:
- 比较常规的组合数,对于每一位iii,我们判断Cn−1i−1C_{n-1}^{i-1}Cn−1i−1是否大于当前的值即可。
- 再套一个高精度。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>inline void rd(T&x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
const int N=302;
int K;
#define L 255
#define P 10000
struct Big{
int num[L],len;
Big(){
memset(num,0,sizeof(num));
len=1;
}
void Rd(){
char A[N];
scanf("%s",A);
int SL=strlen(A);
len=0;
for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=4){
if(i>=3)for(int j=i-3;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
}
}
void Pr(){
if(!num[len-1]){putchar("0");return;}
printf("%d",num[len-1]);
DREP(i,len-2,0)printf("%04d",num[i]);
}
Big operator +(const Big &a)const{
Big b;
b.len=max(len,a.len);
REP(i,0,b.len-1){
int &B=b.num[i];
B+=num[i]+a.num[i];
if(B>=P)B-=P,b.num[i+1]++;
}
if(b.num[b.len])b.len++;
return b;
}
Big operator+(int a){
Big b;
b.len=len;
REP(i,0,len-1){
int &B=b.num[i];
B+=num[i]+a%P;
if(B>=P)b.num[i+1]++,B-=P;
a/=P;
}
if(b.num[b.len])b.len++;
return b;
}
bool operator<=(const Big &a)const{
if(len!=a.len)return len<a.len;
DREP(i,len-1,0) if(num[i]!=a.num[i]) return num[i]<a.num[i];
return 1;
}
}n,now,tmp,C[N][22];
int pos[30];
int get(int k){
for(int i=k;;i++){
tmp=C[i-1][k-1];
if(n<=now+tmp) return i;
else now=now+tmp;
}
}
int main(){
// freopen("num.in","r",stdin);
// freopen("num.out","w",stdout);
n.Rd();
scanf("%d",&K);
C[0][0]=C[0][0]+1;
REP(i,1,300){
C[i][0]=C[i][0]+1;
REP(j,1,min(20,i)) C[i][j]=C[i-1][j-1]+C[i-1][j];
}
SREP(i,0,K) pos[i]=get(K-i);
pos[K]=0;
SREP(i,0,K){
putchar('1');
while(pos[i]-1>pos[i+1])pos[i]--,putchar('0');
}
putchar('\n');
return 0;
}
T2T_2T2——dreaming
Description:
有nnn个点mmm条边的图,现在加n−1−mn-1-mn−1−m条长度为LenLenLen的边使之变为一棵树,求树的直径的最小值。
n≤105n\le10^5n≤105
Solution:
- 比较裸的题了…
- 初始,我们预处理出每个树的直径,求出直径上最佳的中点midmidmid(d=min{max{dis[mid],dis[R]−dis[mid]}}d=min\{max\{dis[mid],dis[R]-dis[mid]\}\}d=min{max{dis[mid],dis[R]−dis[mid]}}),拿这些中点来连边。
- 最优的连边策略,一定是拿拥有最长的ddd的中点与其它中点建边。
- 建完边后再求一遍直径即为答案。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>inline void rd(T&x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
const int N=1e5+2,INF=0x3f3f3f3f;
int n,m;
ll Len;
int qwq,head[N];
struct edge{
int to,nxt;
ll w;
}E[N<<1];
void addedge(int x,int y,ll z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)
struct p100{
ll dis[N];
int fa[N];
ll Mn[N];// 每个小树的 min{max{dis[L][mid],dis[mid][R]}}
int rt[N],cnt;// 每个小树的中点
bool vis[N];
int Id;
ll Mx;
void dfs1(int x,int f,ll d){
if(chkmax(Mx,d)) Id=x;
vis[x]=1;
EREP(x){
int y=E[i].to;
if(y==f)continue;
dfs1(y,x,d+E[i].w);
}
}
void dfs2(int x,int f,ll d){
if(chkmax(Mx,d)) Id=x;
fa[x]=f;
dis[x]=d;
EREP(x){
int y=E[i].to;
if(y==f)continue;
dfs2(y,x,d+E[i].w);
}
}
void solve(){
REP(i,1,n) if(!vis[i]) {
Id=Mx=-1;
dfs1(i,0,0);
rt[++cnt]=Id;//L
Mx=-1;
dfs2(Id,0,0);//R
int R=Id,L=rt[cnt],mid=R;
Mn[cnt]=INF;
if(L==R) Mn[cnt]=0;
while(mid!=L){
if(chkmin(Mn[cnt],max(dis[mid],dis[R]-dis[mid])))rt[cnt]=mid;
mid=fa[mid];
}
}
ll mx1=-1;
int Rt1=-1;
REP(i,1,cnt)if(chkmax(mx1,Mn[i])) Rt1=rt[i];
REP(i,1,cnt){
if(Rt1==rt[i])continue;
addedge(Rt1,rt[i],Len);
addedge(rt[i],Rt1,Len);
}
Mx=Id=-1;
dfs1(1,0,0);
Mx=-1;
dfs2(Id,0,0);
printf("%lld\n",Mx);
}
}p2;
int main(){
// freopen("dreaming.in","r",stdin);
// freopen("dreaming.out","w",stdout);
rd(n),rd(m),rd(Len);
memset(head,-1,sizeof head);
REP(i,1,m){
int a,b,c;
rd(a),rd(b),rd(c);
++a,++b;
addedge(a,b,c);
addedge(b,a,c);
}
p2.solve();
return 0;
}
T3T_3T3——foodshop
Description:
有nnn个点,nnn条边的图,求一个点,使得这个点到其它的点的最大距离最小(注意:这个点可以在边上)。
n≤105,Li≤109n\le10^5,L_i\le10^9n≤105,Li≤109
Solution:
- 因为它是一个nnn个点nnn条边的图。
- 那么它是一个基环树。
- 首先,一定会先考虑分类讨论。我们优先解决环上。
- 对于点在环上时,我们可以发现,最大距离一定是环上与该点对立的点的距离+对立点的子树的最大深度,且这种对立点至多会有2个。
- 那么考虑对立点有2个时,发现这两个对立点之间的边使无用的,即可以删除。
- 那么由此启发,我们可以枚举一条环边删除,那么答案即为剩下的这棵树的直径除2。
- 但枚举环边,这样复杂度是Θ(n2)\Theta(n^2)Θ(n2)的。
- 那么我们考虑数据结构来优化。
- 对于一条要删除的环边(k,k+1)(k,k+1)(k,k+1),我们找直径,由两次dfs找直径启发,我们从kkk点向左找最远距离的点,再从该点找最远的距离,同理,从k+1k+1k+1点向右找。
- 那么由环上距离,我们知道Dist(i,j)=mx[i]+mx[j]+{dis[j]−dis[i],i≤jLen−dis[j]+dis[i],i>jDist(i,j)=mx[i]+mx[j]+\begin{cases} dis[j]-dis[i],i\le j \\ Len-dis[j]+dis[i],i>j\end{cases}Dist(i,j)=mx[i]+mx[j]+{dis[j]−dis[i],i≤jLen−dis[j]+dis[i],i>j
- 我们可以考虑线段树来维护环上每个点到其它点的最大距离。
- 这样复杂度就是Θ(nlogn)\Theta(n\log n)Θ(nlogn)
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>inline void rd(T&x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
const int N=2e5+2;
int n;
int qwq,head[N];
struct edge{
int to,nxt;
ll w;
}E[N<<1];
void addedge(int x,int y,ll z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)
int dfn[N],low[N],tim;
int stk[N<<1],top;
bool vis[N];
int chain[N],len;
bool belong[N];
ll ans;
void tarjan(int x,int f){
dfn[x]=low[x]=++tim;
stk[++top]=x;
bool flag=1;
EREP(x){
int y=E[i].to;
if(y==f and flag){flag=0;continue;}
if(!dfn[y]) tarjan(y,x),chkmin(low[x],low[y]);
else chkmin(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
if(stk[top]!=x){
do {
chain[++len]=stk[top];
belong[stk[top]]=1;
}while(x!=stk[top--]);
}
else top--;
}
}
struct p70{
bool mark[N<<1];
ll Mx,Id;
void dfs1(int x,int f,ll d){
if(chkmax(Mx,d)) Id=x;
EREP(x){
if(mark[i])continue;
if(E[i].to!=f) dfs1(E[i].to,x,d+E[i].w);
}
}
void dfs2(int x,int f,ll d){
if(chkmax(Mx,d)) Id=x;
EREP(x){
if(mark[i])continue;
if(E[i].to!=f) dfs2(E[i].to,x,d+E[i].w);
}
}
void solve(){
ans=1e18;
REP(p,1,len){
int x=chain[p],y=chain[p%len+1];
EREP(x){
if(E[i].to==y) {
mark[i]=mark[i^1]=1;
Mx=Id=-1;
dfs1(1,0,0);
Mx=-1;
dfs2(Id,0,0);
chkmin(ans,Mx);
mark[i]=mark[i^1]=0;
}
}
}
printf("%.1lf\n",1.0*ans/2);
}
}p1;
ll dep[N<<1],dis[N],sum[N<<1];
ll mx[N],Sum[N];
ll Dist(int x,int d){
if(!~x) return -1e18;
return dep[x]+((d==1)?-sum[x]:sum[x]);
}
struct p100{
ll dfs(int x,int f,ll d,int id){
chkmax(dep[id],d);
mx[x]=Sum[x]=0;
EREP(x){
int y=E[i].to;
if(y==chain[id%len+1]) dis[id]=E[i].w;
if(y==f or belong[y])continue;
ll t=dfs(y,x,d+E[i].w,id)+E[i].w;
chkmax(Sum[x],mx[x]+t);
chkmax(mx[x],t);
}
return mx[x];
}
struct Tree{
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
#define family tree[p],tree[p<<1],tree[p<<1|1]
struct node{
int L,R;
int id1,id2;
ll mx1,mx2;
}tree[N<<2];
void Up(node &A,node L,node R){
if(L.mx1>R.mx1)A.mx1=L.mx1,A.id1=L.id1;
else A.mx1=R.mx1,A.id1=R.id1;
if(L.mx2>R.mx2)A.mx2=L.mx2,A.id2=L.id2;
else A.mx2=R.mx2,A.id2=R.id2;
}
void build(int L,int R,int p){
tree[p].L=L,tree[p].R=R;
if(L==R){
tree[p].mx1=Dist(L,1);
tree[p].mx2=Dist(L,2);
tree[p].id1=tree[p].id2=L;
return;
}
int mid=(L+R)>>1;
build(lson),build(rson);
Up(family);
}
int query(int L,int R,int p,int op){
if(L>R)return -1;
if(tree[p].L==L and tree[p].R==R) return op==1?tree[p].id1:tree[p].id2;
int mid=(tree[p].L+tree[p].R)>>1;
if(R<=mid) return query(L,R,p<<1,op);
else if(L>mid) return query(L,R,p<<1|1,op);
else {
int l=query(lson,op),r=query(rson,op);
return Dist(l,op)>Dist(r,op)?l:r;
}
}
}T;
void solve(){
REP(i,1,len){
dfs(chain[i],0,0,i);
sum[i]=sum[i-1]+dis[i-1];
}
REP(i,1,len){
dep[i+len]=dep[i];
if(i>1) sum[i+len]=sum[i+len-1]+dis[i-1];
else sum[i+len]=sum[len]+dis[len];
}
ans=1e18;
T.build(1,len<<1,1);
REP(i,1,len){
int l1=T.query(i,i+len-1,1,1);
int l2=T.query(i,i+len-1,1,2);
int tmp;
ll Mx=0;
if(l1==l2){
int r1=T.query(i,l1-1,1,1);
tmp=T.query(l1+1,i+len-1,1,1);
if(Dist(tmp,1)>Dist(r1,1))r1=tmp;
int r2=T.query(i,l2-1,1,2);
tmp=T.query(l2+1,i+len-1,1,2);
if(Dist(tmp,2)>Dist(r2,2))r2=tmp;
Mx=Dist(l1,1)+Dist(r2,2);
chkmax(Mx,Dist(l2,2)+Dist(r1,1));
}
else Mx=Dist(l1,1)+Dist(l2,2);
chkmin(ans,Mx);
}
REP(i,1,n) chkmax(ans,Sum[i]);
printf("%.1lf\n",1.0*ans/2);
}
}p2;
int main(){
// freopen("foodshop.in","r",stdin);
// freopen("foodshop.out","w",stdout);
rd(n);
memset(head,-1,sizeof head);
REP(i,1,n){
int a,b;ll c;
rd(a),rd(b),rd(c);
addedge(a,b,c);
addedge(b,a,c);
}
tarjan(1,0);
if(n<=3000) p1.solve();
else p2.solve();
return 0;
}