纪中培训 8月8日 day3 考试

描述:

MWH寒假外出旅游,来到了S国。S国划分为N个省,第i个省有Ti座城市,编号分别为Ci1,Ci2,……CiTi(各省城市编号不会重复)。所有城市间有M条双向的道路连接,从任意一个城市出发,可到达一切城市,每条道路均须收费。
     此时恰逢春运期间,S国交通运输局采取了优惠措施。当一条路的路费在[L..R]区间时,可免去。同时,每个省也有优惠措施,第i个省内的每条道路路费收其Xi%,连接第i个省和第j个省的每条道路路费收其(Xi%+Xj%)/2。
MWH想从城市s走到城市t,请求出一对L,R,确保:

  1. MWH能免费到达目的地;
  2. L≤R;
  3. L、R均为整数;
  4. L尽可能地大,R在满足L最大的前提下最小。

输入

第一行两个整数N,M。
接下来M行,每行三个整数,u、v、w,表示连接u、v的道路需收费w。
接下来N行,第i+M+1行有一个整数Ti,后面Ti个整数,分别是Ci1..CiTi(所有城市编号保证按正整数顺序给出1..  Ti)。
下一行N个整数X1..Xi。
最后一行,两个整数,s、t。

输出

一行两个整数,如题,L和R。

样例  

输入                   输出

3 7                           2 6
1 2 3
5 2 8
1 3 7
5 4 5
2 4 9
3 5 10
3 4 2
2 1 2
1 3
2 4 5
30 50 60
1 5

n<=5000  m<=100000 城市<=50000

注意:因每条道路由各省的交通运输局直接管辖,所以每条道路的路费必须先得到省级优惠,再得到国家级优惠。

杨神犇提供显然思路,跑一个最大生成树,s->t路径上最小边就是l,然后跑一个最小生成树,强行使刚才的最小边加入树中,s->t路径上最大边就是r,输出即可。

我的思路是先二分一个l,权值在l~15000的边保留,看s->t是否在同一联通块,并查集判断即可,r同理。

我的代码:

#include <cstdio>
#include <algorithm>
#include <ctime>

using namespace std;
const int N=100005;

int n,m;
int xx[N],yy[N],block[50005],ss,t;
int dl,dr=16000,maxx;
double van[N],zk[50005];
inline int read() { char k=0;char ls;ls=getchar(); for(;ls<'0'||ls>'9';k=ls,ls=getchar()); int x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0'; if(k=='-')x=0-x;return x; }
struct bcj
{
    int fa[50005];
    inline void  be(){for(int i=1;i<=maxx;i++)fa[i]=i;}
    int f(int x){return fa[x]=(fa[x]==x)?x:f(fa[x]);}
    int u(int x,int y){fa[f(y)]=f(x);}
}s;

inline bool gay1(int x)
{
    s.be();
    for(int i=1;i<=m;++i)
    {
        if(x<=van[i]) s.u(xx[i],yy[i]);
    }
    if(s.f(ss)==s.f(t)) return true;
    return false;
}
inline void fuck1()
{
    int l=0,r=16000,ans;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(gay1(mid)) l=mid+1,ans=mid;
        else  r=mid;
    }
    dl=ans;
}

inline bool gay2(int x)
{
    s.be();
    for(int i=1;i<=m;++i)
    {
        if(dl<=van[i]&&van[i]<=x) s.u(xx[i],yy[i]);
    }
    if(s.f(ss)==s.f(t)) return false;
    return true;
}
inline void fuck2()
{
    int l=0,r=16000,ans;
    while(l<r)
    {
        int mid=(l+r)>>1;//printf("mid=%d\n",mid);
        if(gay2(mid)) l=mid+1;//,printf("jzsb:l=%d r=%d mid=%d\n",l,r,mid);
        else r=mid,ans=mid;//,printf("ljjz:l=%d r=%d mid=%d\n",l,r,mid);
    }
    dr=ans;
}
int main()
{
    freopen("trip.in","r",stdin);
    freopen("trip.out","w",stdout);
//    double stt=clock();
    n=read();m=read();
    for(int i=1;i<=m;++i){xx[i]=read(),yy[i]=read(),scanf("%lf",&van[i]);maxx=max(max(xx[i],yy[i]),maxx);}
    for(int i=1,T,x;i<=n;++i)
    {
        T=read();
        while(T--) x=read(),block[x]=i;
    }
    
    for(int i=1;i<=n;++i)
    {
        scanf("%lf",&zk[i]);
        zk[i]/=(double)100;
    }
    for(int i=1;i<=m;++i)
    {
        int jzoj=block[xx[i]];
        int laji=block[yy[i]];
        if(jzoj==laji) 
          van[i]*=zk[jzoj];
        else
          van[i]*=(zk[jzoj]+zk[laji])/2;
    }
    /*for(int i=1;i<=m;++i)
    {
        printf("%d %d %lf\n",xx[i],yy[i],van[i]);
    }*/
    ss=read();t=read();
    fuck1();fuck2();
    printf("%d %d",dl,dr);
//    double eed=clock();
//    printf("\n%.2lfms",eed-stt);
}
遨游
 
描述:
求n全排列字典序第k小是什么?
 
输入一个n
 

Data Constraint

【数据约定】
对于10%的数据:1<=n<=3
对于20%的数据,1<=n<=9
对于30%的数据:1<=n<=18,1<=k<=10^6
对于60%的数据:1<=n<=18
对于80%的数据:1<=n<=100,1<=k<=10^100
对于90%的数据:1<=n<=1000,1<=k<=10^1000
对于100%的数据:1<=n<=100000,1<=k<=min(10^20000,n!)

显然过90%数据高精模拟即可。100%数据发现1<=k<=min(10^20000,n!),大概在n>=6000左右开始模拟即可。

具体先看k的值,来确定第一位放那个:

如:输入 4 8

考虑4的全排列第一位放什么,假如第一个放1,后面3个有(n-1)!种放法。

所以应该放的为k mod (n - i +1)! / (n - i)!    (从大到小枚举i)

然后 k mod (n - i +1)! / (n - i)!=k div (n - i)! mod (n - i + 1)

暴力高精除低精,高精取膜,int64 压位即可。(表示没写);

Description   
  话说, 小X是个数学大佬,他喜欢做数学题。有一天,小X想考一考小Y。他问了小Y一道数学题。题目如下:
      对于一个正整数N,存在一个正整数T(0<T<N),使得

的值是正整数。
      小X给出N,让小Y给出所有可能的T。如果小Y不回答这个神奇的大佬的简单数学题,他学神的形象就会支离破碎。所以小Y求你帮他回答小X的问题。   (n<=10^14)

随便推公式即可(感谢van老师教的数学)。

[n-(t/2)]/(n-t)=k

1/2+n/[2(n-t)]=k

1+n/(n-t)=2k

就是让n/(n-t)为奇数即可,

枚举n的约数即可。(题解只是提示,细节不多提)

#include <cstdio>
#include <algorithm>
#include <cmath> 

using namespace std;


long long n;
long long a[10000005],b[10000005];
long long cnt1,cnt2;
bool flag;
int main()
{
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    scanf("%lld",&n);
    
    if(n==1||n==0||n==2) {printf("0");return 0;}
    int sq=sqrt(n);
    for(long long i=1;i<=sq;++i)
    {
        
        if(n%i==0)
        {
        //    printf("%lld\n",n%i);
            flag=true;
            long long j=n/i;
        //    printf("%lld %lld\n",i,j);
            if((j&1)&&i!=n)
            {
                b[++cnt2]=n-i;
            }
            if((i&1)&&j!=n)
            {
                a[++cnt1]=n-j;
            }
        }
    }
    if(flag==false)
    {
        printf("1 %lld",n-1);
    }
    else
    {
        printf("%lld ",cnt1+cnt2);
        for(int i=1;i<=cnt1;++i) printf("%lld ",a[i]);
        for(int i=cnt2;i>=1;--i) printf("%lld ",b[i]);
    }
}
van

猜你喜欢

转载自www.cnblogs.com/AidenPearce/p/9445894.html