NOIP提高组历年真题题解

2018

铺设道路

差分水题,推一下结论就好了。

#include<cstdio>
#include<algorithm>
using namespace std;
int a[100005],d[100005],ansz,ansf;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),d[i]=a[i]-a[i-1];
    for(int i=1;i<=n;i++)
    {
    if(d[i]<0) ansf-=d[i];
    else ansz+=d[i];        
    }    
    printf("%d\n",max(ansz,ansf));
    return 0;
} 

货币系统

一开始以为是数学题,后来发现可以$dp$,很有意思的完全背包简单变形。

$f$数组下标存储每一个数,$true$表示已经出现,$false$表示不能被表示。

初始化$f[0]$为$true$。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[205],f[25005],T;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
    int ans=0,maxn=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),maxn=max(maxn,a[i]);
    sort(a+1,a+1+n);    
    memset(f,0,sizeof(f));
    f[0]=true;//f的下标代表的是这个数是否出现过 
    for(int i=1;i<=n;i++)
    {
    if(f[a[i]])continue;//如果出现过 continue 
    ans++;//默认没出过 
    for(int j=a[i];j<=maxn;j++)
    f[j]=max(f[j-a[i]],f[j]);//如果j-a[i]出现过,可以保证的是j也能出现,这就是一个true的赋值转移 
    }    
    printf("%d\n",ans);
    }
    return 0;
}

赛道修建

通过这道题我对图论$dfs$过程的理解又加深了许多,其实就是从根节点一直递归到叶节点,得到结果再一层层递归回去,和普通递归无异。一晚上只做了一道题还行..

题意大概就是:给一棵树,选$m$条路径,使最小的路径值最大。

先讲暴力分。(暴力真的是打得比正解还艰难..)

$25pts$:整张图是一条链。($b[i]=a[i]+1$)

问题转化成:有$n-1$个值,需要把这些值划分成$m$个区间,求最小区间的最大值。

裸的二分。

当然要注意一点,要求出正确的链,所以要$dfs$一次。

void dfs(int u,int fa)//预处理
{
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        a[u]=e[i].w;    
    }
}
bool check(int pos)//二分
{
    int now=0,cnt=0;
    for(int i=1;i<n;i++)
    {
        now+=a[i];
        if(now>=pos)cnt++,now=0;
    }
    if(cnt>=m)return true;
    else return false;
}
dfs(1,0);
while(l<=r)
{
    mid=(l+r)>>1;
    if(check(mid))l=mid+1;
    else r=mid-1;
}
printf("%d\n",l-1);    

 $20pts$:菊花图

菊花图就更简单了,从大到小排个序,$a[i]+a[2*m-i+1]$求$min$就可以了。

if(u==1)tot++;
bool cmp(int a,int b){return a>b;}
if(tot==n-1)
{
    int las=0x7f7f7f7f;
    sort(s+1,s+n,cmp);
    for(int i=1;i<=m;i++)
    las=min(las,s[i]+s[2*m-i+1]);
    printf("%d\n",las);
    return 0;
}

$10pts$:$m=1$

求一遍树的直径即可。

void dp(int u)
{
    vis[u]=1;
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(vis[v])continue;
        dp(v);
        ans=max(ans,f[u]+f[v]+e[i].w);
        f[u]=max(f[u],f[v]+e[i].w);
    }
}

$55pts$完整代码

#include<bits/stdc++.h>
using namespace std;
int n,m,l,r,mid,a[100005],h[100005],cnt,f[50005],vis[50005],ans,s[500005],tot;
struct node
{
    int v,w,nxt;
}e[100005];
void add(int u,int v,int w)
{
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=h[u];
    h[u]=cnt;
}
void dfs(int u,int fa)
{
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        a[u]=e[i].w;    
    }
}
bool check(int pos)
{
    int now=0,cnt=0;
    for(int i=1;i<n;i++)
    {
        now+=a[i];
        if(now>=pos)cnt++,now=0;
    }
    if(cnt>=m)return true;
    else return false;
}
void dp(int u)
{
    vis[u]=1;
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(vis[v])continue;
        dp(v);
        ans=max(ans,f[u]+f[v]+e[i].w);
        f[u]=max(f[u],f[v]+e[i].w);
    }
}
bool cmp(int a,int b){return a>b;}
int main()
{
//    freopen("testdata.in","r",stdin);
//    freopen("testdata.out","w",stdout);
    memset(h,-1,sizeof(h));
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        if(u==1)tot++;
        s[i]=w;
        r+=w;
        add(u,v,w),add(v,u,w);
    }
    if(tot==n-1)
    {
        int las=0x7f7f7f7f;
        sort(s+1,s+n,cmp);
        for(int i=1;i<=m;i++)
        las=min(las,s[i]+s[2*m-i+1]);
        printf("%d\n",las);
        return 0;
    }
    else if(m==1)
    {
        dp(1);
        printf("%d\n",ans);
        return 0;
    }
    dfs(1,0);
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",l-1);        
    return 0;
}

$100pts$

看到“最小的最大”,很自然地想到二分,二分一个答案$mid$,每一次$dfs$去累加答案$cnt$。

我们考虑,一条合法的路径要$>=mid$,从任意一个点开始,往下递归,如果遇到了$>=mid$的路径,就$cnt++$,否则就把这条路径暂时存起来,等待这条路径找到和另一条起点相同的路径/边组合在一起成为一条合法路径。

怎么说都是$Talk is cheap$,那就上代码吧,就是个树上$dp$,我用$multiset$过的。

代码中有一些奇怪的东西, 是$sukechao$学长帮我调试的时候加上的,挨骂就挨骂吧,学长$tql$!

#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,h[500005],cnt,ans;
multiset<int>s[500005];
multiset<int>::iterator it;
struct node
{
    int v,w,nxt;
}e[500005<<1];
void add(int u,int v,int w)
{
    e[++cnt].v=v;
    e[cnt].nxt=h[u];
    e[cnt].w=w;
    h[u]=cnt;
}
int tot=0; 
int dfs(int u,int fa,int k)
{
//    cout<<++tot<<"  ";
//    cout<<u<<" 死龟儿cjy"<<endl; 
    s[u].clear();
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v,w=e[i].w;
        if(v==fa)continue;
        int val=dfs(v,u,k)+w;
        if(val>=k)ans++;
        else s[u].insert(val); 
    }
    int maxn=0;
    while(!s[u].empty())
    /*本while循环是一个寻找半链的过程 从最小的s[u].begin()开始 寻找k-s[u].begin()的前驱 如果找到了就ans++
    找不到的话说明s[u].begin()对于半链路径是无用的 就可以用来更新全链路径 也就是往上跳
    类似于一个树上贪心的过程 能找多少路径就找多少条 让每一条路径向着k去靠 就能找到最多的路径 正确性是显而易见的
    而关于是否会重复的问题
    因为我们这棵树是从下到上递归的 所以过程中要么因为找到半链路从multiset中删除并且更新答案
    要么成为全链子节点向下的最优解(必须要选最优解哈) 
    如果全链满足条件 更新答案
    否则就进入set 向上跳
    重复的情况是不会出现的 正确性显然 
    */ 
    {
        if(s[u].size()==1)return max(maxn,*s[u].begin());//无法再寻找半链 可以向上跳 
        it=s[u].lower_bound(k-*s[u].begin());//二分寻找另一条半链 
        if(it==s[u].begin()&&s[u].count(*it)/*==1*/)it++; //找到自己了qwq
        if(it==s[u].end()) 
        {
            maxn=max(maxn,*s[u].begin());//反正你也找不到另一条半链了 向上jump吧
            s[u].erase(*s[u].begin());//begin()的值找不到 证明所有的这个值都找不到 所以你要全部删除  
        }
        else
        {
            ans++;//成功找到半链
            s[u].erase(s[u].begin());
            s[u].erase(it); 
        }
    }
    return maxn;//把选剩下的maxn递归回祖宗 
}
bool check(int pos)
{
    ans=0;
    dfs(1,0,pos);
    if(ans>=m)return true;
    else return false;
}
int l,r,mid,last;
int main()
{
    //freopen("testdata.in","r",stdin);
    memset(h,-1,sizeof(h));
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
        r+=w;
    }
    while(l<=r)
    {
//    cout<<250<<endl;
        tot=0;
        mid=(l+r)>>1;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",l-1);
    return 0;
}

$dfs$的过程我可以这样解释:

首先自然递归到最底层,此时$maxn$会返回一个$0$,如果此时$0+e[i].w>=val$,答案更新,否则进入以本子树根节点为下标的$multiset$。处理了所有的子节点,开始$while$循环,如果能找到任意两个$multiset$里的元素相加$>=mid$,我们就又可以更新路径啦,为了满足贪心,我们从$s[u].begin()$开始寻找,找$mid-s[u].begin()$的前驱即可,找到了就从$multiset$中删除($erase$迭代器而不是值的本身),注意判断存在性,我在这里$wa$了好几次。同时如果某个值找不到对应的前驱,我们可以直接$erase$这个值($erase$这个值会删除所有为这个值的点,参照我$STL$的相关$blog$),因为你留着也没用了。不停找半链,找到就删除两个链的迭代器,找不到就删除所有值和这条链相同的链,当然在这之前应该$max$一波,$size=1$的时候也要max,因为最后要递归回去一个值给父亲节点。这个递归回去的值一定是不能在半链查找中发挥作用的路径的最大值,因为其他没有被选为半链组合的路径不能更优了。

总结下来,我们从最底层往上跳,有$>=mid$的值直接计入答案,否则进入$multiset$,然后在这一层的$multiset$当中找到可以组合为合法路径的半链,找到就$erase$迭代器,找不到就用来更新返回值的$max$值,然后删除所有的这种值的链,然后把这个最大值(其他没有被组合为合法路径的半链不会更优)接到父亲节点上去更新全链,这样就是一个贪心的过程,且正确性显而易见。

旅行

这个题虽然很暴力,不过也很有意思,它又让我加深了自己对递归执行的理解。首先,题中有一个可爱的隐含条件“任意选定一个城市作为起点,然后从起点开始,每次可以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。仔细读这个条件,含义是:我们每一次可以选择向前走到一个没走过的城市,也可以选择后退回到一个走过的城市,但不能前进到走过的城市,也不能后退到没走过的城市。我开始还以为多难呢..

认真读题真的 太 重 要 了!


$60pts$(一棵树)

非常好拿,我们考虑建一个$vector$来存储这张图,从小到大排序就可以了,$30$+行代码轻轻松松水$60$分。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,h[100005],cnt,ans[100005],vis[100005];
vector<int>e[100005];
void dfs(int u)
{
    ans[++cnt]=u;
    vis[u]=true;
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(vis[v])continue;
        dfs(v);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i=1;i<=n;i++)
    sort(e[i].begin(),e[i].end());
    dfs(1);
    for(int i=1;i<=cnt;i++)
    printf("%d ",ans[i]);
    return 0;
}

 

$100pts$(基环树)

也很好拿,在我们遍历的这个过程中,无论我们选择怎么走,最终只会走$n-1$条道路,第$n$条道路就是多余的,你可以选择暴力删除每一条边,也可以先把这个环预处理出来删除环,删除环的方式应该算$Tarjan$找环,也算新学了点知识,找到环之后依次删除环中的每一条边,进行遍历,最后输出每一次遍历的最小字典序。

$qwq$也太暴力了...

$vis[]$数组和$fa$都能判断退出环,但是$vis[]$更保险一点点。

#include<bits/stdc++.h>
using namespace std;
int n,m,h[10005],cnt,ans[10005],temp[10005],fa[10005],dfn[10005],idx,loop[10005],tot,top,vis[10005];
vector<int>ve[5005];
void dfs(int u)
{
    dfn[u]=++idx;
    for(int i=0;i<ve[u].size();i++)
    {
        int v=ve[u][i];
        if(v==fa[u])continue;
        if(dfn[v])
        {
            if(dfn[v]<dfn[u])continue;
            loop[++tot]=v;
            for(;v!=u;v=fa[v])
            loop[++tot]=fa[v];
        }
        else
        {
            fa[v]=u;
            dfs(v);
        }
    }
}
void violent(int u,int pre,int uu,int vv)
{
    temp[++top]=u;
    for(int i=0;i<ve[u].size();i++)
    {
        int v=ve[u][i];
        if(v==pre)continue; 
        if((u==uu&&v==vv)||(u==vv&&v==uu))continue;
        violent(v,u,uu,vv);
    }
}
int main()
{
    memset(ans,0x3f,sizeof(ans));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ve[u].push_back(v);
        ve[v].push_back(u);
    }
    for(int i=1;i<=n;i++)
    sort(ve[i].begin(),ve[i].end());
    if(n==m)
    {
    dfs(1);
    loop[++tot]=loop[1];
    int now=1;
    tot--;
    while(now<=tot)
    {
    //    memset(vis,0,sizeof(vis)); 
        memset(temp,0,sizeof(temp));
        top=0;
        bool flag=false;
        int uu=loop[now],vv=loop[++now];
        violent(1,0,uu,vv);
        for(int i=1;i<=n;i++)
        {
            if(ans[i]==temp[i])continue;
            else
            {
            if(ans[i]<temp[i])flag=false;
            else flag=true;
            break;    
            }
            
        }
        if(flag)
        {
        for(int i=1;i<=n;i++)
        ans[i]=temp[i];            
        }
    }
    for(int i=1;i<=n;i++)
    printf("%d ",ans[i]);    
    return 0;    
    }
    violent(1,0,0,0);
    for(int i=1;i<=n;i++)
    printf("%d ",temp[i]);
    return 0;
}

 

 

 

2017 

小凯的疑惑

这道题正式转化为:巨鱼的疑惑,我没有看任何题解,第一眼就是$exgcd$,居然随便改改就过了...证明我自己都不会写,蒙了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
int a,b,x,y,ans;
int exgcd(int a,int b)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    ans=exgcd(b,a%b);
    int temp=x;
    x=y;
    y=temp-(a/b)*y;
    return ans;
}
signed main()
{
    scanf("%lld%lld",&a,&b);
    exgcd(a,b);
    int A=b/ans,B=a/ans;
    x=(x%A+A)%A,y=(y%B+B)%B;
    printf("%lld\n",a*x+b*y-a-b-1);
    return 0;
}

 2016

组合数问题

大水题,主要是我一直不知道杨辉三角的这个性质..

若用$C$表示组合数,则$C(n,m)=C(n-1,m-1)+C(n-1,m)$,这个式子也是杨辉三角的式子,通常适用于n,m比较小的情况。预处理$c[i][i]=c[i][0]=1$。

这个题取模就可以,如果$%$的结果为$0$,则代表它是满足条件的,乱搞就行,然后写一个二维前缀和,基本就解决了。

#include<cstdio>
int T,n,m,k,a[2005][2005],s[2005][2005],f[2005][2005];
int main()
{
    scanf("%d%d",&T,&k);
    for(int i=0;i<=2001;i++)
    a[i][i]=1,a[i][0]=1;
    for(int i=1;i<=2000;i++)
        for(int j=1;j<=i;j++)
        {
            a[i][j]=(a[i-1][j]+a[i-1][j-1])%k;
            if(a[i][j]==0)f[i][j]=1;
        }
    for(int i=1;i<=2000;i++)
        for(int j=1;j<=2000;j++)
        s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+f[i][j];
    while(T--)
    {
        scanf("%d%d",&n,&m);
        printf("%d\n",s[n][m]);
    }
    return 0;
}

玩具谜题 

#include<bits/stdc++.h>
using namespace std;
int n,m,cd[100005],tot=0;
bool qz[100005],fx[100005],temp;
string s[100005];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        cin>>qz[i]>>s[i];
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&fx[i],&cd[i]);
    }
    tot=1;
    temp=qz[tot];
    for(int i=1;i<=m;i++)
    {
        if(temp==false)
        {
            if(fx[i]==false)
            {
                tot-=cd[i];
                if(tot<=0) tot+=n;
                temp=qz[tot];
            }
            else if(fx[i]==true)
            {
                tot+=cd[i];
                if(tot>n) tot-=n;
                temp=qz[tot];
            }
        }
        else if(temp==true)
        {
            if(fx[i]==false)
            {
                tot+=cd[i];
                if(tot>n) tot-=n;
                temp=qz[tot];
            }
            else if(fx[i]==true)
            {
                tot-=cd[i];
                if(tot<=0) tot+=n;
                temp=qz[tot];
            }
        }
    }
    cout<<s[tot]<<endl;
    return 0;
}

 

2015

神奇的幻方

水题。

#include<cstdio>
using namespace std;
int n;
struct node{int x,y;}a[8008];
int vis[45][45];
int main()
{
    scanf("%d",&n);
    a[1].x=1,a[1].y=(n+1)>>1;
    vis[1][(n+1)>>1]=1;
    int cnt=2;
    while(cnt<=n*n)
    {
        if(a[cnt-1].x==1&&a[cnt-1].y!=n)a[cnt].x=n,a[cnt].y=a[cnt-1].y+1,vis[a[cnt].x][a[cnt].y]=cnt;
        if(a[cnt-1].x!=1&&a[cnt-1].y==n)a[cnt].x=a[cnt-1].x-1,a[cnt].y=1,vis[a[cnt].x][a[cnt].y]=cnt;
        if(a[cnt-1].x==1&&a[cnt-1].y==n)a[cnt].x=a[cnt-1].x+1,a[cnt].y=a[cnt-1].y,vis[a[cnt].x][a[cnt].y]=cnt;
        if(a[cnt-1].x!=1&&a[cnt-1].y!=n)
        {
            if(vis[a[cnt-1].x-1][a[cnt-1].y+1])a[cnt].x=a[cnt-1].x+1,a[cnt].y=a[cnt-1].y,vis[a[cnt].x][a[cnt].y]=cnt;
            else a[cnt].x=a[cnt-1].x-1,a[cnt].y=a[cnt-1].y+1,vis[a[cnt].x][a[cnt].y]=cnt;
        }
        cnt++;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        printf("%d ",vis[i][j]);    
        printf("\n");    
    }
    return 0;
}

跳石头

二分答案。

#include<bits/stdc++.h>
using namespace std;
const int N=50005;
int a[N],d,n,m;
inline int read(){//我喜欢快读
    int num = 0;
    char c;
    bool flag = false;
    while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
        if (c == '-') flag = true;
    else
        num = c - '0';
    while (isdigit(c = getchar()))
    num = num * 10 + c - '0';
    return (flag ? -1 : 1) * num;
}
int check(int x)
{
    int prev=0,next=0;
    int tot=0;
    while(next<n+1)
    {
        next++;
        if(a[next]-a[prev]<x) ++tot;
        else prev=next;
    }
    return tot;
}
int main()
{
    d=read();
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
    }
    a[0]=0;
    a[n+1]=d;
    int l=1,r=d;
//    int ans=0;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(check(mid)<=m) l=mid;
        else r=mid-1;
    }
    printf("%d\n",l);
    return 0;
}

 

2014

生活大爆炸版石头剪刀布

#include<bits/stdc++.h>
using namespace std;
int a,b,n,sa,sb,aa[205],bb[205],k,i=0,j=0;
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int w=1;w<=a;w++)
    {
        scanf("%d",&k);
        aa[w]=k;
    }
    for(int w=1;w<=b;w++)
    {
        scanf("%d",&k);
        bb[w]=k;
    }
    for(int w=1;w<=n;w++)
    {
            i++;
            j++;
            if(i==a+1) i=1;
            if(j==b+1) j=1;
            if(aa[i]==0&&bb[j]==1)sb++;
            if(aa[i]==0&&bb[j]==2)sa++;
            if(aa[i]==0&&bb[j]==3)sa++;
            if(aa[i]==0&&bb[j]==4)sb++;
            if(aa[i]==1&&bb[j]==0)sa++;
            if(aa[i]==1&&bb[j]==2)sb++;
            if(aa[i]==1&&bb[j]==3)sa++;
            if(aa[i]==1&&bb[j]==4)sb++;
            if(aa[i]==2&&bb[j]==0)sb++;
            if(aa[i]==2&&bb[j]==1)sa++;
            if(aa[i]==2&&bb[j]==3)sb++;
            if(aa[i]==2&&bb[j]==4)sa++;
            if(aa[i]==3&&bb[j]==0)sb++;
            if(aa[i]==3&&bb[j]==1)sb++;
            if(aa[i]==3&&bb[j]==2)sa++;
            if(aa[i]==3&&bb[j]==4)sa++;
            if(aa[i]==4&&bb[j]==0)sa++;
            if(aa[i]==4&&bb[j]==1)sa++;
            if(aa[i]==4&&bb[j]==2)sb++;
            if(aa[i]==4&&bb[j]==3)sb++;        
    }
    printf("%d %d\n",sa,sb);
    return 0;
}

2013

货车运输

题意很明晰,$Kruskal$求最小生成树+树上倍增求区间最大值。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int tot;
//#pragma GCC optimize(3)
//#pragma -fcrossjumping
//#pragma -fdefer-pop
//#pragma -fmerge-constans
//#pragma -fthread-jumps
//#pragma -floop-optimize
//#pragma -fif-conversion
//#pragma -fif-conversion2
//#pragma -fdelayed-branch
//#pragma -fguess-branch-probability
//#pragma -fcprop-registers
//#pragma -fforce-mem
//#pragma -foptimize-sibling-calls
//#pragma -fstrength-reduce
//#pragma -fgcse
//#pragma -fcse-follow-jumps
//#pragma -frerun-cse-after-loop
//#pragma -fdelete-null-pointer-checks
//#pragma -fextensive-optimizations
//#pragma -fregmove
//#pragma -fschedule-insns
//#pragma -fsched-interblock
//#pragma -fcaller-saves
//#pragma -fpeephole2
//#pragma -freorder-blocks
//#pragma -fstrict-aliasing
//#pragma -funit-at-a-time
//#pragma -falign-functions
//#pragma -fcrossjumping
//#pragma -finline-functions
//#pragma -fweb
int n,m,Q;
struct Kruskal
{
    int u,v,w;
}k[60005];
struct node
{
    int v,nxt,w;
}e[20005];
int h[10005],fa[10005],cnt,p[10005][25],w[10005][25],d[10005],vis[10005];
bool cmp(Kruskal a,Kruskal b){return a.w>b.w;}
int find(int x){return fa[x]==x?x:find(fa[x]);}
int min(int x,int y)
{
    if(x>y)return y;
    else return x;
}
void add(int u,int v,int w)
{
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=h[u];
    h[u]=cnt;
}
void kruskal()
{
    for(int i=1;i<=m;i++)
    {
        int x=find(k[i].u);
        int y=find(k[i].v);
        if(x==y)continue;
        tot++;
        
        fa[x]=y;
        add(k[i].u,k[i].v,k[i].w);
        add(k[i].v,k[i].u,k[i].w);
        if(tot==n)break;
    }
}
void dfs(int u,int fa)
{
    vis[u]=1;
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(v==fa)continue;
        w[v][0]=e[i].w;
        p[v][0]=u;
        dfs(v,u);
    }
}
int lca(int a,int b)
{
    int ans=0x7f7f7f7f;
    if(d[a]>d[b])swap(a,b);
    for(int i=17;i>=0;i--)
    if(d[a]<=(d[b]-(1<<i)))ans = min(ans,w[b][i]),b=p[b][i];
    if(a==b)return ans;
    for(int i=17;i>=0;i--)
    {
        if(p[a][i]==p[b][i])continue;
        ans = min(ans,min(w[a][i],w[b][i]));
        a=p[a][i],b=p[b][i];
    }
    return min(ans,min(w[a][0],w[b][0]));
}
inline void read(int&x)
{
    x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
}
inline void print(int x)
{    
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
   
}
int main()
{
    memset(w,0x7f7f7f7f,sizeof(w));
    memset(h,-1,sizeof(h));
    read(n),read(m);
    for(int i=1;i<=m;i++)
    read(k[i].u),read(k[i].v),read(k[i].w);
    sort(k+1,k+1+m,cmp);
    for(int i=1;i<=n;i++)fa[i]=i;
    kruskal();
    for(int i=1;i<=n;i++)
    if(!vis[i])dfs(i,i);
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i<=n;i++)
    if(p[i][j-1])p[i][j]=p[p[i][j-1]][j-1],w[i][j]=min(w[i][j-1],w[p[i][j-1]][j-1]);           
    read(Q);
    while(Q--)
    {
        int x,y;
        read(x),read(y);
        if(find(x)!=find(y))print(-1),printf("\n");
        else print(lca(x,y)),printf("\n");
    }
    return 0;
}

 

 

2012

同余方程

#include<cstdio>
using namespace std;
int x,y,a,b;
int exgcd(int a,int b)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    int ans=exgcd(b,a%b);
    int temp=x;
    x=y;
    y=temp-(a/b)*y;
    return ans;
}
int main()
{
    scanf("%d%d",&a,&b);
    int gcdx=(b/exgcd(a,b));
    int gcdy=(a/exgcd(a,b));
    x=(x%gcdx+gcdx)%gcdx;
    y=(y%gcdy+gcdy)%gcdy;
    printf("%d\n",x);
    return 0;
}

2011

铺地毯

#include<bits/stdc++.h>
using namespace std;
int xa[10005],xi[10005],ya[10005],yi[10005],x,y,a,b,c,d,flag=0,n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        xi[i]=a;
        xa[i]=a+c;
        yi[i]=b;
        ya[i]=b+d;
    }
    scanf("%d%d",&x,&y);
    for(int i=1;i<=n;i++)
    {
        if(xi[i]<=x&&xa[i]>=x&&yi[i]<=y&&ya[i]>=y)
        flag=i;
    }
    if(flag)
    {
        printf("%d\n",flag);
        return 0;
    }
    else printf("-1");
    return 0;
}

聪明的质监员

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+5;
long long n,m,w[N],v[N],l[N],r[N],sv[N],snum[N],S,maxn=-1,minn=0x7ffffff,Y,sum,ans;
bool check(int W)
{
    memset(snum,0,sizeof(snum));
    memset(sv,0,sizeof(sv));
    Y=0;
    sum=0;
    for(int i=1;i<=n;i++)
    {
    if(w[i]>=W) snum[i]=snum[i-1]+1,sv[i]=sv[i-1]+v[i];    
    else snum[i]=snum[i-1],sv[i]=sv[i-1];    
    }
    for(int i=1;i<=m;i++)
    Y+=((snum[r[i]]-snum[l[i]-1])*(sv[r[i]]-sv[l[i]-1]));
    sum=llabs(Y-S);
    if(Y>S) return true;
    else return false;

}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&S);
    for(int i=1;i<=n;i++)
    {
    scanf("%lld%lld",&w[i],&v[i]);    
    maxn=max(maxn,w[i]);
    minn=min(minn,w[i]);
    }
    for(int i=1;i<=m;i++)
    scanf("%lld%lld",&l[i],&r[i]);
    long long end=maxn+2,beg=minn-1;
    ans=0x3f3f3f3f3f3f3f3f;
    while(beg<=end)
    {
        long long mid=(beg+end)>>1;
        if(check(mid)) beg=mid+1;
        else end=mid-1;
        ans=min(ans,sum);
    }
    ans=min(ans,sum);
    printf("%lld\n",ans);
    return 0;
}

 

2010

机器翻译

#include<bits/stdc++.h>
using namespace std;
int n,m,flag[1005],num[1005],sum,tot=0,dic[1005];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&num[i]);
    }
    memset(flag,0,sizeof(flag));
    for(int i=1;i<=m;i++)
    {
        if(flag[num[i]]==0)
        {
            if(tot<n)
            {
            tot++;
            dic[tot]=num[i];
            flag[num[i]]=1;
            sum++;                
            }
            else
            {
            dic[tot+1]=num[i];
            sum++;
            flag[dic[1]]=0;
            flag[num[i]]=1;
            for(int i=1;i<=n;i++)    
            {
            dic[i]=dic[i+1];    
            }            
            }
        }
    }
    printf("%d\n",sum);
    return 0;
}

 

2005

谁拿了最多奖学金

#include<bits/stdc++.h>
using namespace std;
int main()
{int n;
cin>>n;
int q[200],b[200],l[200],m[100]={0},sum=0,max=0;
string j[n],zn=" ";
char y1[100],y2[100];
for(int i=0;i<n;i++)
{cin>>j[i]>>q[i]>>b[i]>>y1[i]>>y2[i]>>l[i];}
for(int i=0;i<n;i++)
{if(q[i]>80&&l[i]>=1)m[i]+=8000;
if(q[i]>85&&b[i]>80)m[i]+=4000;
if(q[i]>90)m[i]+=2000;
if(q[i]>85&&(y2[i]=='Y'))m[i]+=1000;
if(b[i]>80&&(y1[i]=='Y'))m[i]+=850;
if(m[i]>max){max=m[i];zn=j[i];}
sum+=m[i];
}
cout<<zn<<endl<<max<<endl<<sum<<endl;
return 0;
}

2004

津津的储蓄计划

#include<stdio.h>
int main()
{
    int i,month,left=0,save=0,sum,plan,flag=0;
    for(month=1;month<=12;month++)
    {    left+=300;
        scanf("%d",&plan);
        left = left - plan;
        if(left<0)
        {    flag=1;
            break;
        }
        else
        {i = left/100;
         save += (100*i);
         left -= (100*i);
        }
    }
    if(flag)
        printf("-%d",month);
    else
    {    sum=((1.2*save) + left);
        printf("%d",sum);
    }
    return 0;
}

2002

均分纸牌

#include<bits/stdc++.h>
using namespace std;
int l,r,n,a[10005],ave,sum,step;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    scanf("%d",&a[i]);    
    sum+=a[i];    
    }
    ave=sum/n;
    for(int i=1;i<=n;i++)
    a[i]-=ave;
    l=1;r=n;
    while(a[l]==0&&l<n) l++;
    while(a[r]==0&&r>1) r--;
    while(l<r)
    {
        a[l+1]+=a[l];
        a[l]=0;
        step++;
        l++;
        while(a[l]==0&&l<r) l++;
    }
    printf("%d\n",step);
    return 0;
}

1998

拼数

#include<iostream>
#include<string>
#include <algorithm>
using namespace std;
int jc(int a)    //排列数公式。
{
int i,m=1;
for(i=1;i<=a;i++)
m*=i;
return m;
}
int main()
{
    int n;
    string m,k,a[50];
    k='0';
    cin>>n;
    int i;
    for(i=0;i<n;i++)
    cin>>a[i];
    for(i=0;i<jc(n);i++)  //所有排法。
    {
    m=a[0];
    for(int q=1;q<n;q++)
    m=m+a[q];
    if(m>k)
    k=m;
    next_permutation(a,a+n);
    }
    cout<<k;
        return 0;
}

 

posted @ 2019-10-26 15:38  haruka酱  阅读(683)  评论(2编辑  收藏  举报
浏览器标题切换
浏览器标题切换end