国庆长乐酱油之旅day1

T1:叉叉

爆零之路的起点.....

考场上的代码有一个数组的下标敲错,然后爆零

自测几组数据竟然还过了......

emmmm

Code:

2018-10-01 19:51:01成功A掉
评价:普及+,提高- 
思路:
首先很容易想到如何判断两条线有交点
就是在所给的字符串中找到两个一样的点
然后在这两个点之间搜索
记录两点之间所有点出现的次数,判断是否为偶数即可
然而这是片面的
我们需要找两个一样的点也是有限制的,我们要处理一下。 
同时我们在记录交点的时候,不能单纯的凭借奇偶来判断,要采用特殊的方法
讲上面的那些废话只是因为基本思路比较重要....... 
当然代码处理起来也特难受 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define size 100010

using namespace std;

string a;
int num[30],start[size],end[size],pos[size],posn[size],vis[size];
//num相当于一个桶,用于存26个字母出现的次数
//start存区间头,end存区间尾;
//pos和posn都存顺序,具体作用代码中解释 
int sign[size],ans;

int main()

{
    cin>>a;
    int len=a.length();
    
    for(int i=0;i<len;i++)
        sign[i]=a[i]-96;
        //把字母转换成数字来做 
        
    int tot=0;//用于统计区间的个数 
    
    //求出每一个区间的头和尾 
    for(int i=0;i<len;i++)
    //枚举每一个字母 
    {
        if(num[sign[i]]==0)
        //如果这个数是0,即可以当做一个区间的开头 
        {
            tot++; 
            start[tot]=i;//记录区间头 
            num[sign[i]]=1;//标记这里被当做开头 
            pos[sign[i]]=tot;// 记录下 第几组区间 
            posn[i]=tot;
        }
        else
        //如果这个数不是0,即找到了区间的结尾 
        {
            num[sign[i]]=0;//回溯,取消标记 
            end[pos[sign[i]]]=i;//对应的当前这一组的区间 
            posn[i]=pos[sign[i]];//记录 
        }
    }
    
    for(int i=1;i<=tot;i++)
    //开始枚举每一个区间 
    {
        int sum=0;
        memset(vis,0,sizeof(vis));
        for(int j=start[i]+1;j<end[i];j++)
        //枚举区间中间的数 
        {
            if(vis[posn[j]]==0)//如果没被访问过 
            {
                vis[posn[j]]=1;//标记 
                sum++;//统计答案 
            }
            else//如果之前访问过,即前面有字母与之对应 
            {
                vis[posn[j]]=0; //取消标记 
                sum--;
            }
        }
        ans+=sum;//累加答案 
    }
    
    printf("%d",ans/2);//因为在计算的时候回多算,所以要 /2 
    
    return 0;
}
//By Yfengzi 

纯净Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define size 100010

using namespace std;

string a;
int num[30],start[size],end[size],pos[size],posn[size],vis[size];
int sign[size],ans;

int main()

{
    cin>>a;
    int len=a.length();
    
    for(int i=0;i<len;i++)
        sign[i]=a[i]-96; 
        
    int tot=0;
    for(int i=0;i<len;i++)
    {
        if(num[sign[i]]==0)
        {
            tot++;
            start[tot]=i;
            num[sign[i]]=1;
            pos[sign[i]]=tot;
            posn[i]=tot;
        }
        else
        {
            num[sign[i]]=0;
            end[pos[sign[i]]]=i;
            posn[i]=pos[sign[i]];
        }
    }
    
    for(int i=1;i<=tot;i++)
    {
        int sum=0;
        memset(vis,0,sizeof(vis));
        for(int j=start[i]+1;j<end[i];j++)
        {
            if(vis[posn[j]]==0)
            {
                vis[posn[j]]=1;
                sum++;
            }
            else
            {
                vis[posn[j]]=0;
                sum--;
            }
        }
        ans+=sum;
    }
    
    printf("%d",ans/2);
    
    fclose(stdin);
    fclose(stdout);
    
    return 0;
}

T2:HSY回家

不在状态,再次爆零.....

跑最短路就可以了

跑的时候判断一下即可

良心的出题人并没有卡spfa

所以我们跑spfa

Code:

//2018-10-06 21:51:41正式切掉 
//spfa就行了吧,照常求最短路,中间判断走不走特殊道路即可
//如果要走,那走几次最好,这在做spfa时都是可以记录的
//设f[i][j]表示走i此通道,到达j点的值 
//如果通道的数量多余传送的次数,那我们就可以直接做spfa,中间处理一下他的 i 就行了,对于 j 我们直接搞spfa; 
//如果通道的数量少于传送的次数,那就是裸的spfa了,因为如果传送次数多了,那就可能造成多余的浪费,还不如不走; 
 
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define size 5005

using namespace std;

bool special[size];
int n,m,q,k,ans;
int tot,head[size],ver[size],next[size],edge[size];

void add(int x,int y,int z,bool love)
{
    tot++;
    ver[tot]=y;
    edge[tot]=z;
    special[tot]=love;
    next[tot]=head[x];
    head[x]=tot;
}

bool vis[600];
int f[2001][601];
queue<int> qwq;
//qwq 

void spfa1()
{
    //spfa 
    memset(f,0x7f,sizeof(f));
    f[0][1]=0;
    qwq.push(1);
    vis[1]=1;
    while(!qwq.empty())
    {
        int cur=qwq.front();
        qwq.pop();
        vis[cur]=0;
        for(int i=head[cur];i;i=next[i])
        {
            int x=ver[i];
            //判断走传送通道的次数 
            for(int j=0;j+special[i]<=k;j++)
            {
                if(f[j][cur]==0x7f7f7f7f) break;//如果cur这个点还没处理 
                if(f[j+special[i]][x]>f[j][cur]+edge[i])//如果走传送通道 到x这个点比不走传送通道大 
                {
                    f[j+special[i]][x]=f[j][cur]+edge[i];//转移 
                    if(vis[x]==0)
                    {
                        vis[x]=1;
                        qwq.push(x); 
                    }
                }
            }
        }
    }
}

//与spfa1基本一样,只是f的第一维全部变为0,因为我们不用传送通道 
void spfa2()
{
    memset(f,0x7f,sizeof(f));
    f[0][1]=0;
    qwq.push(1);
    vis[1]=1;
    while(!qwq.empty())
    {
        int cur=qwq.front();
        qwq.pop();
        vis[cur]=0;
        for(int i=head[cur];i;i=next[i])
        {
            int x=ver[i];
            if(f[0][x]>f[0][cur]+edge[i])
            {
                f[0][x]=f[0][cur]+edge[i];
                if(vis[x]==0)
                {
                    vis[x]=1;
                    qwq.push(x);
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&q,&k);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z,0);
    }
    for(int i=1;i<=q;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z,1);
    }
    
    if(q>=k)
    {
        spfa1();
        int ans=0x7f7f7f7f;
        for(int i=0;i<=k;i++)
            ans=min(ans,f[i][n]);
        printf("%d",ans==0x7f7f7f7f?-1:ans);
    }
    else
    {
        spfa2();
        printf("%d",f[0][n]);
    }
    
    return 0;
}
//By Yfengzi

T3:砍掉树边

基础树形dp

//基础树形dp

\(f[i]\)代表使以\(i\)为根的树不连通的最小代价

易得转移方程

\(f[i]=min(edge[i],f[j])\)

即使得这棵树与叶子结点不连通的方法有两种

一种是删除与叶子节点相连的边,即\(edge[i]\)

一种是删除包括这些叶子结点的子树即 \(f[j]\)

Code:

//2018-10-08 10:17:30切掉
//基础树形dp
//f[i]代表使以i为根的树不连通的最小代价
//易得转移方程
//f[i]=min(edge[i],f[j])
//即使得这棵树与叶子结点不连通的方法有两种
//一种是删除与叶子节点相连的边,即edge[i] 
//一种是删除包括这些叶子结点的子树即 f[j]
#include<iostream>
#include<cstring>
#include<cstdio> 
#define size 100010

using namespace std;

int n,s,f[size];
bool vis[size];
int tot,ver[size*2],edge[size*2],head[size],next[size*2];

void add(int x,int y,int z)
{
    tot++;
    next[tot]=head[x];
    edge[tot]=z;
    ver[tot]=y;
    head[x]=tot;
}

void dfs(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        int y=ver[i];
        if(vis[y]) continue;
        dfs(y);
        if(f[y]==0) f[x]+=edge[i];
        //如果到达了边界,即到了遍历到了树的倒数第二层,这时只能删边 
        else f[x]+=min(f[y],edge[i]);
        //dp 
    }
}

int main()
{
    scanf("%d%d",&n,&s);
    for(int i=1;i<=n-1;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    
    dfs(s);
    
    printf("%d",f[s]);
    
    return 0;
}
//By Yfengzi 

T4:阶乘求零

emmmm

数论...

直接把阶乘求出来是不可能的,所以我们要从\(n\)\(k\)的关系入手

对于\(k\)进制下的数,要使其末尾为零,肯定要\(k\)的因数相乘。

那么我们考虑将\(k\)质因数分解

\(n!\)含几个\(k\)因数,然后将\(k\)包含的该质因子的个数除以\(n!\)中包含该质因子的个数,然后选出这个最小值即可

证明:

对于样例 \(n=10,k=10\)来说

\(k=2\times5=10\)

\(n!\)中包含8个2和2个5,则\(n!\)末尾有2个0,因为,其8个2除以\(k\)的一个2,得到8,然后其2个5除以\(k\)的一个5,得到2,即\(n!\)最多可以用两个2和两个5来组成k的倍数.

注意开long long!

Code:

//2018-10-08 11:14:10切掉 
#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

const long long INF = ((1ll << 62) - 1 << 1) + 1;
long long n,k,s,tt;
long long tot1,tot2;
long long ans=INF;

int main()
{
    scanf("%lld%lld",&n,&k);
    s=sqrt(k);
    for(int i=2;i<=s;i++)//将k质因数分解 
    {
        if(k%i) continue;
        tot1=0;tot2=0;
        while(k%i==0)//统计质因子i在k总出现的次数 
        {
            tot1++;
            k/=i;
        }
        tt=n;
        while(tt) //统计质因子i在n!中出现的次数 
        {
            tot2+=tt/i;
            tt/=i;
        }
        ans=min(ans,tot2/tot1);
    } 
    if(k>1)//处理最后的一个质因数
    {
        tot2=0;
        tt=n; 
        while(tt) 
        {
            tot2+=tt/k;
            tt/=k;
        }
        ans=min(ans,tot2);
    }
    
    printf("%lld",ans);
    
    return 0;
}
//By Yfengzi

T5T6 已死亡

猜你喜欢

转载自www.cnblogs.com/fengzi8615/p/9758260.html
今日推荐