NOIP模拟题 2016.9.3 [数论] [逆序对] [树状数组] [树形dp]

强迫症
问题描述
人行道铺着两行地砖,第一行每块的长度是A/B,第二行每块的长度是X/Y。两行砖块
第一块的一边是对齐的。
作为一个强迫症患者,看到这样的地砖你很不爽,于是就想知道,最少隔多少距离后两
行地砖的缝隙又会对齐。
输入格式
输入第一行包含一个整数T,表示测试点组数。
接下来T 行,每行两个分数,格式为A/B X/Y,两个分数中间用一个空格隔开。
输出格式
T 行,每行包含一个分数(若答案为整数则输出整数),表示每组数据的答案。分数必
须以最简形式输出。
样例输入
2
3/2 5/8
4/3 3/10
样例输出
15/2
12
数据范围
30%的数据A,B,X,Y<=20
70%的数据T<=10
100%的数据1<=A,B,X,Y,<=10,000,T<=100,000


这个求分数的lcm就好了。
ans = [A/B,X/Y] = [AY,BX]/BY.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
LL gcd(LL a,LL b)
{
    if(!b) return a;
    return gcd(b,a%b);
}
LL lcm(LL a,LL b) {return a/gcd(a,b)*b;}
void reduce(LL &a,LL &b)
{
    LL GCD = gcd(a,b);
    a/=GCD; b/=GCD;
}
void print(LL a,LL b)
{
    if(b==1) printf(AUTO,a);
    else printf(AUTO"/"AUTO,a,b);
    putchar('\n');
}
LL A,B,X,Y;
int main()
{
    freopen("tile.in","r",stdin);
    freopen("tile.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf(AUTO"/"AUTO AUTO"/"AUTO,&A,&B,&X,&Y);
        LL numer = lcm(A*Y,B*X);
        LL deno = B*Y;
        reduce(numer,deno);
        print(numer,deno);
    }
    return 0;
}

手套
问题描述
你现在有N对手套,但是你不小心把它们弄乱了,需要把它们整理一下。N对手套被一字排开,每只手套都有一个颜色,被记为0~N-1,你打算通过交换把每对手套都排在一起。由于手套比较多,你每次只能交换相邻两个手套。请你计算最少要交换几次才能把手套排整齐。
输入格式
输入第一行一个N,表示手套对数。
第二行有2N个整数,描述了手套的颜色。每个数都在0~N-1之间,且每个数字都会出现恰好两次。
输出格式
一行,包含一个数,表示最少交换次数。
样例输入
2
0 1 0 1
样例输出
1
数据范围
30%的数据N≤9;
60%的数据N≤1000;
100%的数据N≤200,000。


这个要把手套放到一起,那么先给手套按出现顺序标号,然后求达到目标状态 {1,1,2,2,3,3,…,n-1,n-1,n,n}所产生的逆序对即可。
这么做是为了尽可能的减少交换次数,保证答案最优。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
#define lowbit(x) ((x)&(-x))
const int maxn = 400005;
int sum[maxn<<2];
int n;
inline void add(int x,int val)
{
    for(int i=x;i<=n;i+=lowbit(i))
        sum[i]+=val;
}
inline int query(int x)
{
    int ret=0;
    for(int i=x;i;i-=lowbit(i))
        ret += sum[i];
    return ret;
}
int color[maxn];
int a[maxn];
int match[maxn],cur;
void init()
{
    scanf("%d",&n); n<<=1;
    for(int i=1;i<=n;i++) scanf("%d",color+i);
    for(int i=1;i<=n && cur<=(n>>1);i++)
    {
        if(match[color[i]]) continue;
        match[color[i]]=++cur;
    }
    for(int i=1;i<=n;i++) a[i]=match[color[i]];
}
LL calc()
{
    LL ret=0;
    for(int i=1;i<=n;i++)
    {
        ret += i-query(a[i])-1;
        add(a[i],1);
    }
    return ret;
}
int main()
{
    freopen("gloves.in","r",stdin);
    freopen("gloves.out","w",stdout);
    init();
    printf(AUTO,calc());
    return 0;
}

星座
问题描述
星空中有n颗星星,有n-1对星星间被人为地连上了线,每条连线有各自的长度。所有星星被连成了一个整体。现在,你要在星系中找到一个最大的十字形星座。即,你要找到两条星星构成的路径,使得它们恰好有一颗公共星(这颗公共星不能是某条路径的端点),且两条路径的长度和最大。
左图红线表示了一个合法的十字形星座,而右图的星座并不合法。
输入格式
第一行一个数n,表示星星的数量。
接下来n行,每行3个数x,y,z,表示第x颗星星和第y颗星星间有一条连线,它的长度是z。
输出格式
一行,包含一个整数,表示最大的路径长度和。若答案不存在,输出-1。
样例输入
10
3 8 6
9 3 5
1 9 2
4 8 6
2 3 3
10 4 8
5 9 5
7 2 3
6 9 1
样例输出
33
数据范围
20%的数据n<=1000
50%的数据n<=10,000
100%的数据n<=100,000,0<=z<=1000


这题跟上次那个沿着父亲走的那道树规有点儿像。。
对于每一个可能成为的交叉点的点,第一种情况是这个点下面四条最大的路径之和,第二种情况是这个点下面三条路径与父亲连通的路径之和,那么就可以dp了。
维护dp[u][0,1,2,3]为u以下四条最大路径,f[u]为父亲所连最长路径,转移的话dp数组每个儿子找一下即可,f数组的话,可能是由f[fa[u]]得到,也可能是dp[fa[u]][0],这里注意如果最大路径是由当前v得到,就用dp[fa[u]][1]。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 100005;
struct Edge
{
    int to,next;
    int val;
}edge[maxn<<1];
int head[maxn];
int maxedge;
int deg[maxn];
inline void addedge(int u,int v,int c)
{
    edge[++maxedge] = (Edge) { v,head[u],c };
    head[u] = maxedge;
    edge[++maxedge] = (Edge) { u,head[v],c };
    head[v] = maxedge;
    deg[u]++; deg[v]++;
}
int n,root;
void init()
{
    scanf("%d",&n); root=(n+1)>>1;
    memset(head,-1,sizeof(head)); maxedge=-1;
    for(int i=1;i<n;i++)
    {
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        addedge(u,v,c);
    }
}
int dp[maxn][4],from[maxn][4],f[maxn];
struct Node
{
    int val,id;
    Node(const int _val,const int _id) { val=_val; id=_id; }
    bool operator < (const Node t) const { return val >= t.val; }
};
void dfs1(int u,int father)
{
    int child=0;
    for(int i=head[u];~i;i=edge[i].next)
        if(edge[i].to^father) { child++; break; }
    if(!child) { dp[u][0]=0; return; }
    priority_queue <Node> que;
    int cnt=0;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father) continue;
        dfs1(v,u);
        if(~dp[v][0])
            if(cnt<4 || que.top().val<dp[v][0]+edge[i].val)
                que.push(Node(dp[v][0]+edge[i].val,v)),cnt++;
    }
    while(cnt>4) que.pop(),cnt--;
    while(cnt)
    {
        cnt--;
        Node t=que.top(); que.pop();
        dp[u][cnt]=t.val;
        from[u][cnt]=t.id;
    }
}
void dfs2(int u,int father)
{
    int child=0;
    for(int i=head[u];~i;i=edge[i].next)
        if(edge[i].to^father) { child++; break; }
    if(!child) return;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father) continue;
        int son=-1;
        for(int k=0;k<4;k++)
            if(from[u][k]^v) { son=dp[u][k]; break; }
        if(!~son) son=0;
        f[v]=max(f[u],son)+edge[i].val;
        dfs2(v,u);
    }
}
inline bool bad_case()
{
    for(int i=1;i<=n;i++)
        if(deg[i]>=4) return false;
    return true;
}
int work()
{
    if(bad_case()) return -1;
    memset(dp,-1,sizeof(dp));
    dfs1(root,-1);
    dfs2(root,-1);
    int ret=0;
    for(int i=1;i<=n;i++)
    {
        int t1=0,t2=0;
        if(~dp[i][3]) t1=dp[i][0]+dp[i][1]+dp[i][2]+dp[i][3];
        if(~dp[i][2] && f[i]) t2=f[i]+dp[i][0]+dp[i][1]+dp[i][2];
        smax(ret,max(t1,t2));
    }
    return ret;
}
int main()
{
    freopen("cross.in","r",stdin);
    freopen("cross.out","w",stdout);
    init();
    printf("%d",work());
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值