UPC第38,39,40场部分题解

UPC第38,39,40场部分题解

第38场

A. Bovine Alliance

Description

Bessie and her bovine pals from nearby farms have finally decided that they are going to start connecting their farms together by trails in an effort to form an alliance against the farmers. The cows in each of the N (1 <= N <= 100,000) farms were initially instructed to build a trail to exactly one other farm, for a total of N trails. However months into the project only M (1 <= M < N) of these trails had actually been built. Arguments between the farms over which farms already built a trail now threaten to split apart the cow alliance. To ease tension, Bessie wishes to calculate how many ways the M trails that exist so far could have been built. For example, if there is a trail connecting farms 3 and 4, then one possibility is that farm 3 built the trail, and the other possibility is that farm 4 built the trail. Help Bessie by calculating the number of different assignments of trails to the farms that built them, modulo 1,000,000,007. Two assignments are considered different if there is at least one trail built by a different farm in each assignment.
给出n个点m条边的图,现把点和边分组,每条边只能和相邻两点之一分在一组,点可以单独一组,问分组方案数。

Input

​ Line 1: Two space-separated integers N and M

  • Lines 2…1+M: Line i+1 describes the ith trail. Each line contains two space-separated integers u_i and v_i (1 <= u_i, v_i <= N, u_i != v_i) describing the pair of farms connected by the trail. Note that there can be two trails between the same pair of farms.

Output

  • Line 1: A single line containing the number of assignments of trails to farms, taken modulo 1,000,000,007. If no assignment satisfies the above conditions output 0.

Sample Input

5 4

1 2

3 2

4 5

4 5

Sample Output

6

HINT

OUTPUT DETAILS: There are 6 possible assignments. Letting {a,b,c,d} mean that farm 1 builds trail a, farm 2 builds trail b, farm 3 builds trail c, and farm 4 builds trail d, the assignments are: {2, 3, 4, 5} {2, 3, 5, 4} {1, 3, 4, 5} {1, 3, 5, 4} {1, 2, 4, 5} {1, 2, 5, 4}

显然要用到dfs求连通块,累计方法时注意使用乘法原理,当我们求出一个连通块后,要看点数和边数之间的关系,依题意只有两种成立,点数和边数相等的时候,是一个环,所以有两种,点数=边数+1时,有点数种,也就是说你把其中一个点孤立起来,其他正常连起来。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200010;
struct edge
{
    int next ,to;
}e[N];
int head[N],num=1,vis1[N],vis2[N];  //vis1标记点,vis2标记边
void add(int from,int to)  //邻接表存图
{
    e[++num].next=head[from];
    e[num].to=to;
    head[from]=num;
}
ll  v,e1;
void dfs(int u)
{
    v++;
    vis1[u]=1;
    for(int i=head[u];i;i=e[i].next)
    {
        if(!vis2[i])
        {
            vis2[i]=1;
            vis2[i^1]=1; // 将用过的边标记
            e1++;
            if(!vis1[e[i].to])
            {
                dfs(e[i].to); //搜索下一个点
            }
        }
    }

}
int main()
{
    int n,m,x,y;
    cin >> n >>m;
    while(m--)
    {
        cin >> x >> y;
        add(x,y);
        add(y,x);
    }
    ll ans=1;
    for(int i=1;i<=n;i++)
    {
        if(!vis1[i])
        {
            v=0;
            e1=0;
            dfs(i);
            if(e1>v)
            {
                cout << 0 << endl;    //注意这时无法满足题目条件
                return 0;
            }
            if(v==e1) ans=ans*2%mod;
            else ans=ans*v%mod; 
        }
    }
    cout << ans ;
    return 0;
}

B.Grazing Patterns

Due to recent budget cuts, FJ has downsized his farm so that the grazing area for his cows is only a 5 meter by 5 meter square field! The field is laid out like a 5x5 grid of 1 meter by 1 meter squares, with (1,1) being the location of the upper-left square, and (5,5) being the location of the lower-right square:
(1,1) (1,2) (1,3) (1,4) (1,5)
(2,1) (2,2) (2,3) (2,4) (2,5)
(3,1) (3,2) (3,3) (3,4) (3,5)
(4,1) (4,2) (4,3) (4,4) (4,5)
(5,1) (5,2) (5,3) (5,4) (5,5)

Every square in this grid is filled with delicious grass, except for K barren squares (0 <= K <= 22, K even), which have no grass. Bessie the cow starts grazing in square (1,1), which is always filled with grass, and Mildred the cow starts grazing in square (5,5), which also is always filled with grass.

Each half-hour, Bessie and Mildred finish eating all the grass in their respective squares and each both move to adjacent grassy squares (north, south, east, or west). They want to consume all the grassy squares and end up in exactly the same final location. Please compute the number of different ways this can happen. Bessie and Mildred always move onto grassy squares, and they never both move onto the same square unless that is the very last grassy square remaining.

给你一个5*5方格,有一些点不能走,求不重复走且走完所有可以走的方格,从起点走到终点的所有方案数。

输入

* Line 1: The integer K.
* Lines 2…1+K: Each line contains the location (i,j) of a non-grassy square by listing the two space-separated integers i and j.

输出

* Line 1: The number of different possible ways Bessie and Mildred can walk across the field to eat all the grass and end up in the same final location.

样例输入

4
3 2
3 3
3 4
3 1

样例输出

1

把题看懂,无脑dfs,注意递归函数中结束的条件是走到终点且把能走的全走。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#define ll long long
using namespace std;
const int N=200010;
int n,m,k,a,b,c,fl,t,x,y;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int vis[10][10];
int ans=0;
void dfs(int x,int y,int sum)
{
    if(x==5&&y==5&&sum==25)
    {
        ++ans;
        return;
    }
    for(int i=0;i<4;++i)
    {
       int xx=x+dx[i];
       int yy=y+dy[i];
       if(xx>=1&&xx<=5&&yy>=1&&yy<=5&&!vis[xx][yy])
       {
           vis[xx][yy]=1;
           dfs(xx,yy,sum+1);
           vis[xx][yy]=0;
       }
    }
}
int main()
{
    cin >> k;
    for(int i=1;i<=k;++i)
    {
       cin >> x >> y;
       vis[x][y]=1;
    }
    vis[1][1]=1;
    dfs(1,1,k+1);
    cout << ans ;
    return 0;
}

C. Mountain Climbing

Farmer John has discovered that his cows produce higher quality milk when they are subject to strenuous exercise. He therefore decides to send his N cows (1 <= N <= 25,000) to climb up and then back down a nearby mountain!

Cow i takes U(i) time to climb up the mountain and then D(i) time to climb down the mountain. Being domesticated cows, each cow needs the help of a farmer for each leg of the climb, but due to the poor economy, there are only two farmers available, Farmer John and his cousin Farmer Don. FJ plans to guide cows for the upward climb, and FD will then guide the cows for the downward climb. Since every cow needs a guide, and there is only one farmer for each part of the voyage, at most one cow may be climbing upward at any point in time (assisted by FJ), and at most one cow may be climbing down at any point in time (assisted by FD). A group of cows may temporarily accumulate at the top of the mountain if they climb up and then need to wait for FD’s assistance before climbing down. Cows may climb down in a different order than they climbed up.

Please determine the least possible amount of time for all N cows to make the entire journey.

农场主约翰发现他的奶牛剧烈运动后产奶的质量更高,所以他决定让N头(1 <= N <= 25,000)奶牛去附近爬山再返回来。第i头奶牛用时U(i)爬上山,用时D(i)下山。作为家畜,奶牛们每段路都要有农夫的帮助,可是由于经济疲软,农场里只有两个农夫John和Don。John计划引导奶牛爬山,Don引导奶牛下山。虽然每个奶牛都需要向导,但每段旅途只有一名农夫。所有任何时刻只有一头奶牛爬山也只能有一头奶牛下山,奶牛爬上山后,可以暂时停留在山顶上等待Don的帮助。奶牛上山的顺序和下山的顺序不一定要相同。

请计算出所有N 头牛完成旅程的最短时间。

输入

* Line 1: The number of cows, N.
* Lines 2…1+N: Line i+1 contains two space-separated integers: U(i) and D(i). (1 <= U(i), D(i) <= 50,000).

输出

* Line 1: A single integer representing the least amount of time for all the cows to cross the mountain.

样例输入

3
6 4
8 1
2 3

样例输出

17

显然网上一些结论为max(总上山时间+最快奶牛下山时间,总下山时间+最快奶牛上山时间)是错误的,这种事下意识认为第一头奶牛上山后,上下山都是一起进行的,无疑是错误的。

我们根据每头奶牛上下山的时间可以将奶牛们分为2类:

1.up<down ,2.down>up 相等的随意;

之后,考虑到up小的先到山顶不会拖慢后面的牛,我们把第一类都排在第二类前面,而且按up升序排;第二类牛我们按down降序排,这个没上面那个显然,但是原因也很简单,下的慢的先下可以拖住后面的牛下山,减少出现山顶的牛已经下完了,下面的牛还没上完这种浪费的情况;

排序后 ,O(n)模拟一下计算时间。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include<queue>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=70010;
struct node
{
    int up,down;
    static  bool cmp(const node &a ,const node &b)  //排序
    {
        if(a.up<a.down)
        {
            if(b.up<b.down)
            {
                return a.up<b.up;
            }
            else
            {
                return 1;
            }
        }
        else
        {
            if(b.up<b.down)
            {
                return 0;
            }
            else
            {
                return a.down>b.down;
            }
        }
    }
}jk[N];
int up[N],down[N];
int main()
{
    int n;
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        cin >> jk[i].up >> jk[i].down;
    }
    sort(jk+1,jk+n+1,node::cmp);
    for(int i=1;i<=n;i++)
    {
        up[i]=up[i-1]+jk[i].up;  //前缀和累计上山时间
    }
    for(int i=1;i<=n;i++)
    {
        down[i]=max(down[i-1],up[i])+jk[i].down;  //down[i]为i头奶牛到达地面的时间,max是因为完全可以出现等待的情况
    }
    cout << down[n];
    return 0;
}

E.2nd Greatest Distance

There are N houses numbered 1 through N on a two-dimensional plane. House i is at (xi,yi).

We use Chebyshev distance to calculate the distance between two houses. That is, the distance between Houses i and j is max(|xi−xj|,|yi−yj|).

There are N(N−1)/2 pairs formed by two different houses. For each of these pairs, we will calculate the distance between the houses, and then we will sort these distances in descending order to get a sequence of length N(N−1)/2. Find the second value from the beginning of this sequence.

Constraints
All values in input are integers.
3≤N≤2×105

求max(|xi−xj|,|yi−yj|)次大值。

输入

Input is given from Standard Input in the following format:
N
x1 y1

xN yN

输出

Print the second value from the beginning of the sequence of distances of different houses sorted in descending order.

样例输入

【样例1】
3
0 0
1 2
4 0
【样例2】
4
0 0
0 0
1 0
0 1
【样例3】
20
407 361
167 433
756 388
-551 -47
306 -471
36 928
338 -355
911 852
288 70
-961 -769
-668 -386
-690 -378
182 -609
-677 401
-458 -112
184 -131
-243 888
-163 471
-11 997
119 544

样例输出

【样例1】
3
【样例2】
1
【样例3】
1766

SB题,改吐了。按x排一遍序,取前后各两个,把他们组合一下计算出数值存进新数组,之后标记用过。再按y排个序,还是取前后个两个,之后注意看有没有之前用过的组合,不要算重复。之后将新数组排序取次大就好了。

至于为什么在第一种时把所有组合都算一算,是因为方便标记。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#define ll long long
using namespace std;
const int N=200010;
int n,m,k,a,b,c,fl,t,x,y;
int l[20],l1[10];
int vis[N];
struct node
{
    int x;
    int y;
    int id;
}jk1[N],jk2[N];
bool cmp1(node x,node y)
{
     if(x.x==y.x)
     {
         return x.y<y.y;
     }
     return x.x<y.x;
}
bool cmp2(node x,node y)
{
     if(x.y==y.y)
     {
         return x.x<y.x;
     }
     return x.y<y.y;
}
int main()
{
   int n;
   cin >> n;
   for(int i=1;i<=n;i++)
   {
       scanf("%d %d",&x,&y);
       jk1[i].x=x;
       jk2[i].x=x;
       jk1[i].y=y;
       jk2[i].y=y;
       jk1[i].id=i;
       jk2[i].id=i;
   }
     sort(jk1+1,jk1+n+1,cmp1);
     int k=1;
     l[++k]=max(abs(jk1[1].x-jk1[n].x),abs(jk1[1].y-jk1[n].y));
     vis[jk1[1].id]=1;
     vis[jk1[2].id]=1;
     vis[jk1[n-1].id]=1;
     vis[jk1[n].id]=1;
    l[++k]=max(abs(jk1[1].x-jk1[n-1].x),abs(jk1[1].y-jk1[n-1].y));

    if(n!=3)
    {
        l[++k]=max(abs(jk1[2].x-jk1[n-1].x),abs(jk1[2].y-jk1[n-1].y));
    }
    l[++k]=max(abs(jk1[2].x-jk1[n].x),abs(jk1[2].y-jk1[n].y));
    l[++k]=max(abs(jk1[n].x-jk1[n-1].x),abs(jk1[n].y-jk1[n-1].y));
    l[++k]=max(abs(jk1[1].x-jk1[2].x),abs(jk1[1].y-jk1[2].y));
   sort(jk2+1,jk2+n+1,cmp2);

   if(!vis[jk2[2].id]||!vis[jk2[n].id])
   l[++k]=max(abs(jk2[2].x-jk2[n].x),abs(jk2[2].y-jk2[n].y));
   if(!vis[jk2[1].id]||!vis[jk2[n].id])
   l[++k]=max(abs(jk2[1].x-jk2[n].x),abs(jk2[1].y-jk2[n].y));
   if(n!=3)
   {
        if(!vis[jk2[2].id]||!vis[jk2[n-1].id])
       l[++k]=max(abs(jk2[2].x-jk2[n-1].x),abs(jk2[2].y-jk2[n-1].y));
   }
    if(!vis[jk2[1].id]||!vis[jk2[n-1].id])
   l[++k]=max(abs(jk2[1].x-jk2[n-1].x),abs(jk2[1].y-jk2[n-1].y));
  // cout << endl;
   sort(l+1,l+k+1);
   //cout << k << endl;
   cout << l[k-1] << endl;
   //cout << l[7] << endl;
   return 0;
}

L: 组合数问题

​ 组合数img表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3)三个物品中选择两个物品可以有(1, 2), (1, 3), (2, 3)这三种选择方法。根据组合数的定义,我们可以给出计算组合数[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mnni3KaZ-1628990998962)(http://exam.upc.edu.cn/upload/image/20161230/20161230141422_72219.jpg)] 的一般公式:

img

其中n! = 1×2×…×n。
小葱想知道如果给定n, m和k,对于所有的0≤i≤n,0≤ j≤min(i,m)有多少对(i, j)满足img是k的倍数

​ 3≤n,m ≤2000,2≤k≤21,1≤t≤10000

求组合数杨辉三角打表,再用前缀和优化一下。

c o d e : code: code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#define ll long long
using namespace std;
const int N=3000;
int n,m,k,a,b,c,fl,t,x,y;
ll f[N][N],sum[N][N];
int main()
{
   cin >> t >> k;
   for(int i=1;i<=2005;i++)  //初始化
   {
       f[i][1]=i%k;
       f[i][i]=1;
   }
   for(int i=2;i<=2005;i++)  // 杨辉三角打表
        for(int j=2;j<i;j++)
            f[i][j]=(f[i-1][j]+f[i-1][j-1])%k;
   for(int i=1;i<=2005;i++) //前缀和优化
   {
       for(int j=1;j<=i;j++)
       {
           if(f[i][j]==0) sum[i][j]=sum[i][j-1]+1;
           else sum[i][j]=sum[i][j-1];
       }
   }
   while(t--)
   {
       ll ans=0;
       scanf("%d %d",&x,&y);
       for(int i=1;i<=x;i++)
       {
           if(i>y) ans+=sum[i][y];
           else ans+=sum[i][i];
       }
       printf("%lld\n",ans);
   }
   return 0;
}

第39场

A.Rope Folding

题目描述

Farmer John has a long rope of length L (1 <= L <= 10,000) that he uses for various tasks around his farm. The rope has N knots tied into it at various distinct locations (1 <= N <= 100), including one knot at each of its two endpoints.

FJ notices that there are certain locations at which he can fold the rope back on itself such that the knots on opposite strands all line up exactly with each-other:

img

Please help FJ count the number of folding points having this property. Folding exactly at a knot is allowed, except folding at one of the endpoints is not allowed, and extra knots on the longer side of a fold are not a problem (that is, knots only need to line up in the areas where there are two strands opposite each-other). FJ only considers making a single fold at a time; he fortunately never makes multiple folds.

一个线段,有一些特殊的点,求有多少种折叠方法可以使折叠后特殊点一一对应,注意端点处不能折。

输入

* Line 1: Two space-separated integers, N and L.
* Lines 2…1+N: Each line contains an integer in the range 0…L specifying the location of a single knot. Two of these lines will always be 0 and L.

输出

* Line 1: The number of valid folding positions.

样例输入

5 10
0
10
6
2
4

样例输出

4

枚举这条线段所有点,注意不一定在整点折,可以在1.5这种地方折,所有枚举时候要考虑两种情况。

c o d e : code: code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=20010;
int n,a[N],x,m;
int check(int i)
{
    int ans=0;
    int k=0,fl=0;
    while(i>=k&&i+k<=m)  //注意结束条件,一端到头。
    {
        if(a[i-k]!=a[i+k]) 
        {
            fl=1;
            break;
        }
        k++;
    }
    if(!fl&&i!=m)
    {
        ans++;
    }
    fl=0,k=0;
    while(i>=k+1&&i+k<=m)
    {
        if(a[i-k-1]!=a[i+k])
        {
            fl=1;
            break;
        }
        k++;
    }
    if(!fl) ans++;
    return ans;
}
int main()
{
    cin >> n >> m;
    for(int i=1;i<=n;i++)
    {
        cin >> x;
        a[x]=1; // 标记
    }
    int sum=0;
    for(int i=1;i<=m;i++)
    {
        sum+=check(i);
    }
    cout << sum << endl; 
    return 0;
}

B. Insurance

Snuke has read his own fortune for tomorrow, and learned that there are N scenarios that can happen, one of which will happen tomorrow with equal probability. The i-th scenario will cost him Ai yen (Japanese currency).

Following this, Snuke has decided to get insurance today. If he pays x yen to his insurance company, he will get compensation of min(Ai,2x) yen when Ai yen is lost. Here, he can choose any non-negative real number as x.

Snuke wants to minimize the expected value of the amount of money he loses, which is x+Ai−min(Ai,2x). Find the minimized value.

Constraints
1≤N≤105
1≤Ai≤109
All values in input are integers.

输入

Input is given from Standard Input in the following format:
N
A1 A2 ⋯ AN

输出

Print the answer. Your answer will be judged correct when its absolute or relative error is at most 10−6.

样例输入

【样例1】
3
3 1 4
【样例2】
10
866111664 178537096 844917655 218662351 383133839 231371336 353498483 865935868 472381277 579910117

样例输出

【样例1】
1.83333333333333333333
【样例2】
362925658.10000000000000000000

数学题,我们要算的是:
K ∗ N + ∑ i = 1 n A [ i ] − m a x ( 2 K , A [ i ] ) ; K*N+\sum^n_{i=1}A[i]-max(2K,A[i]); KN+i=1nA[i]max(2K,A[i]);
求上面的最小值,函数应该是个下凸的,应该在 x = k / 2 x=k/2 x=k/2 取的,k应该是a[i]的中位数。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200010;
int n;
double a[N],sum;
int main()
{
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        cin >> a[i];
    }
    sort(a+1,a+n+1);
    if(n==1)  //注意特判
    {
         printf("%.7f",a[1]/2.0);
    }
    else if(n==2)
    {
        printf("%.7f",max(a[1],a[2])/2.0);
    }
    else
    {
        double k;
        if(n%2==1)
        {
            k=a[n/2+1]/2.0;
        }
        else
        {
            k=(a[n/2]+a[n/2+1])/4.0;
        }
      //  cout << k << endl;
        for(int i=1;i<=n;i++)
        {
            sum+=(a[i]-min(2*k,a[i]*1.0));
        }
        //cout << sum << endl;
        printf("%.7f",k+sum/n); //这里不要憨,k*n就爆了,化简一下。
    }

    return 0;
}

D.Many Formulae

题目描述

You are given a sequence of N non-negative integers: A1,A2,⋯,AN.

Consider inserting a + or - between each pair of adjacent terms to make one formula.

There are 2N−1 such ways to make a formula. Such a formula is called good when the following condition is satisfied:

  • - does not occur twice or more in a row.

Find the sum of the evaluations of all good formulae. We can prove that this sum is always a non-negative integer, so print it modulo (109+7).

Constraints
1≤N≤105
1≤Ai≤109
All values in input are integers.

给你一个数组,你可以在相邻两位之间选择+和-,并算出一个结果,最后所有可能的结果的和。

输入

Input is given from Standard Input in the following format:
N
A1 A2 ⋯ AN

输出

Print the sum modulo (109+7).

样例输入

【样例1】
3
3 1 5
【样例2】
4
1 1 1 1
【样例3】
10
866111664 178537096 844917655 218662351 383133839 231371336 353498483 865935868 472381277 579910117

样例输出

【样例1】
15
【样例2】
10
【样例3】
279919144

一眼DP,定义 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 为第i位前是加号的方案贡献总和,就是我们要求的数, d p [ i ] [ 1 ] dp[i][1] dp[i][1]就是第i位是减号的方案贡献总和。 s u m [ i ] [ 0 ] , s u m [ i ] [ 1 ] sum[i][0],sum[i][1] sum[i][0],sum[i][1]分别为第i位前是加号的方案和。

显然有如下方程:

       sum[i][0]=((sum[i-1][0]%mod+sum[i-1][1]%mod)%mod+mod)%mod;
       dp[i][0]=((dp[i-1][0]%mod+dp[i-1][1]%mod)%mod+(sum[i][0]%mod*a[i])%mod)%mod;
       sum[i][1]=sum[i-1][0]%mod;//两个减号不能连着
       dp[i][1]=(dp[i-1][0]%mod-(sum[i][1]*a[i])%mod+mod)%mod;

code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200010;
ll dp[N][3];
ll sum[N][3];
ll a[N];
int main()
{
    int n;
    cin >> n;
    // dp[i][0]=dp[i-1][1]+dp[i-1][0];
   //dp[i][1]=dp[i-1][0];
   for(int i=1;i<=n;i++)
   {
       cin >> a[i];
   }
   if(n==1)
   {
       cout << a[1]%mod;
       return 0;
   }
   dp[2][0]=((a[1]+a[2])%mod+mod)%mod;
   dp[2][1]=((a[1]-a[2])%mod+mod)%mod;
   sum[2][1]=1;
   sum[2][0]=1;
   for(int i=3;i<=n;i++)
   {
       sum[i][0]=((sum[i-1][0]%mod+sum[i-1][1]%mod)%mod+mod)%mod;
       dp[i][0]=((dp[i-1][0]%mod+dp[i-1][1]%mod)%mod+(sum[i][0]%mod*a[i])%mod)%mod;
     //  dp[i][0]%=mod;
       sum[i][1]=sum[i-1][0]%mod;
       dp[i][1]=(dp[i-1][0]%mod-(sum[i][1]*a[i])%mod+mod)%mod;
   }
   cout << (dp[n][1]+dp[n][0])%mod;
    return 0;
}

I.triples I

Doctor Elephant is testing his new program: output the bitwise or of the numbers inputed.
He has decided to input several multiples of 3 and the output of the program should be his favorite number a.
Because he is lazy, he decided to input as few numbers as possible. He wants you to construct such an input for every aa he chose.
It’s guaranteed that for every aa in the input there is such a solution. If there’re multiple solutions you can output any.

题目给出T组样例,每组包含一个数,要求分解为一些3的倍数的数字或操作的和,而其中这些数尽可能少,输出这些数(保证有解,解不一定唯一)

输入

There’re multiple test cases in a test file.
The first line contains a positive integer T - the number of test cases.
In each of the following T lines there is one positive integer a.

输出

For each test case output a line. First you need to output the number of numbers in your input, and then you need to output these numbers, separating by spaces.

样例输入

2
3
7

样例输出

1 3
2 3 6

思路:既然是按位或,那么肯定和二进制有关。显然,一个数的二进制中,奇数位上的1%3=1,偶数位上的1%3=2。那么我们就统计奇数位,偶数位上的个数并用数组记下。首先,我们最多用2个数肯定能凑出这个数。然后分类讨论。

1.a%3==0,显然就是只用a这一个数即可,否则肯定要用两个数。

2.a%3==1,我们就要想办法删去这个1。再次分类讨论:

(1)奇数位上的1和偶数位上的1都不为0,那么我们只需删去第一个奇数位上的1构造出第一个数(这个数肯定是3的倍数),然后再用上删去的那个数和第一个偶数位上的数构造出第二个数((1+2)%3==0,也肯定是3的倍数)。

(2)奇数位上的1不为0,偶数位上的1为0。那么我们也是只需删去第一个奇数位上的1构造出第一个数,再用上删去的那个数和其后的前2个奇数位的数(也就是用前三个奇数位上的数,(1+1+1)%3==0,是3的倍数)。

(3)奇数位上的1为0,偶数位上的1不为0。那么我们只需删去前两个偶数位上的数构造出第一个数((2+2)%31,删去后肯定是3的倍数),再用上删去的那两个数和其后的第一个偶数位的数(也就是用前三个偶数位上的数,(2+2+2)%30,是3的倍数)。

3.a%3==2,同理,我们要想办法删去这个2。再次分类讨论:

(1)奇数位上的1和偶数位上的1都不为0,那么我们只需删去第一个偶数位上的1构造出第一个数(这个数肯定是3的倍数),然后再用上删去的那个数和第一个奇数位上的数构造出第二个数((1+2)%3==0,也肯定是3的倍数)。

(2)奇数位上的1为0,偶数位上的1不为0。那么我们也是只需删去第一个偶数位上的1构造出第一个数,再用上删去的那个数和其后的前2个偶数位的数(也就是用前三个偶数位上的数,(2+2+2)%3==0,是3的倍数)。

(3)奇数位上的1不为0,偶数位上的1为0。那么我们只需删去前两个奇数位上的数构造出第一个数((1+1)%32,删去后肯定是3的倍数),再用上删去的那两个数和其后的第一个奇数位的数(也就是用前三个奇数位上的数,(1+1+1)%30,是3的倍数)。

注意:一定要用上删去的那若干个数,不然构造出两个数按位或后就不等于a。
这个转载至 https://blog.csdn.net/birdmanqin/article/details/97613063

c o d e : code: code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include<queue>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=50010;
ll num1[N],num2[N],bb[N];
int main()
{
    ll t,n;

    scanf("%lld",&t);
    while(t--)
    {
       scanf("%lld",&n);
       ll sum1=0,sum2=0;
       ll k1=0,k2=0;
       memset(num1,0,sizeof(num1));
       memset(num2,0,sizeof(num2));
     //  memset(bb,0,sizeof(bb));
       if(n%3==0)
       {
           printf("1 %lld\n",n);
           continue;
       }
       int cnt=0;
       for(int i=0;i<=60;i++)
		{
			if(n&(1LL<<i)) //二进制枚举快
			{
				if((i+1)&1) num1[++k1]=(1LL<<i);
				else num2[++k2]=(1LL<<i);
			}
		}
     //  cout << k1 << " " << k2 << endl;
       if(n%3==1)
       {
           if(k1>=2) printf("2 %lld %lld\n", n-num1[1],n-num1[2]);
           else if(k1==1) printf("2 %lld %lld\n", n-num1[1],num1[1]+num2[1]);
           else printf("2 %lld %lld\n", n-num2[1]-num2[2],num2[3]+num2[1]+num2[2]);
       }
       else
       {
           if(k2>=2) printf("2 %lld %lld\n", n-num2[1],n-num2[2]);
           else if(k2==1) printf("2 %lld %lld\n", n-num2[1],num1[1]+num2[1]);
           else printf("2 %lld %lld\n", n-num1[1]-num1[2],num1[3]+num1[1]+num1[2]);
       }
    }
    return 0;
}

L.meeting2019

A new city has just been built. There’re nn interesting places numbered by positive numbers from 1 to n.
In order to save resources, only exactly n−1 roads are built to connect these nn interesting places. Each road connects two places and it takes 1 second to travel between the endpoints of any road.
There is one person in each of the places numbered x1,x2…xk and they’ve decided to meet at one place to have a meal. They wonder what’s the minimal time needed for them to meet in such a place. (The time required is the maximum time for each person to get to that place.)

给你n个点的一棵树,结点编号1到n,有k个关键点,在树上找一个点,使得这k个关键点到这个点最远的距离最小,然后输出这个点到这k个关键点的最远距离。

输入

First line two positive integers, n,k - the number of places and persons.(1≤n≤105)
For each the following n−1 lines, there’re two integers a,b that stand for a road connecting place a and b. It’s guaranteed that these roads connected all n places.
On the following line there’re kk different positive integers x1,x2…xk separated by spaces. These are the numbers of places the persons are at.

输出

A non-negative integer - the minimal time for persons to meet together.

样例输入

4 2
1 2
3 1
3 4
2 4

样例输出

2

问题等价于求树的直径,最小距离即为直径除2向上取整。

有两种求法,一是动态规划,对于每个结点,把所有子结点的 d ( i ) d(i) d(i) (表示根为 i i i 的子树中根到叶子的最大距离)都求出来,设 d d d 值前两大的结点为 u u u v v v,则 d ( u ) + d ( v ) + 2 d(u) + d(v) +2 d(u)+d(v)+2就是树的直径。

另一种是两次DFS,从任意一个关键节点开始,找到离它最远的关键结点 y y y,从 y y y 出发dfs找到新的最远结点 z z z,形成的就是直径。

c o d e : code: code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include<queue>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200010;
struct node
{
    int u,v,next;
}e[N];
int num,p,vis[N],maxx;
int head[N],n,m;
void add(int u,int v)
{
    e[++num].u=u;
    e[num].v=v;
    e[num].next=head[u];
    head[u]=num;
}
void dfs(int u,int pre ,int sum)
{
    if(sum>maxx&&vis[u])
    {
        maxx=sum;
        p=u;
    }
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==pre) continue; //防止成环
        dfs(v,u,sum+1);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    cin >> n >> m;
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin >> u >> v;
        add(u,v);
        add(v,u);
    }

    while(m--)
    {
        int x;
        cin >> x;
        vis[x]=1;
    }
    dfs(1,0,0);
    maxx=0;
    dfs(p,0,0);
    cout << (maxx+1)/2;
    return 0;
}

第40场

E: Arithmetic Sequence

Given is a sequence of three integers A=(A1,A2,A3). On this sequence, you can do the following operation any number of times:
choose i∈{1,2,3} and add 1 to Ai.
Find the minimum number of operations needed to make A arithmetic. Here, the sequence A=(A1,A2,A3) is arithmetic when A2−A1=A3−A2 holds.

给你三个数,你可以执行把一个数加一任意次,问要达到 A2−A1=A3−A2最少多少次

Constraints
1≤A1,A2,A3≤1015

输入

Input is given from Standard Input in the following format:
A1 A2 A3

输出

Print the answer.

样例输入

【样例1】
4 8 10
【样例2】
10 3 4
【样例3】
1 2 3
【样例4】
1000000000000000 1 1000000000000000

样例输出

Increasing Triples【样例1】
2
【样例2】
4
【样例3】
0
【样例4】
999999999999999

$A_2−A_1=A_3−A_2 $ 也就是 2 A 2 = A 1 + A 3 2A_2=A_1+A_3 2A2=A1+A3 所以这里我们分两种情况讨论:

​ 1. 2 A 2 > A 1 + A 3 2A_2>A_1+A_3 2A2>A1+A3像这种我们只需要执行 2 A 2 − A 1 − A 3 2A_2-A_1-A_3 2A2A1A3次;

​ 2. 2 A 2 < A 1 + A 3 2A_2<A_1+A_3 2A2<A1+A3时,我们要看 A 1 + A 3 A_1+A_3 A1+A3是不是偶数,不是偶数,执行 A 2 − ( A 1 + A 3 + 1 ) / 2 + 1 A_2-(A_1+A_3+1)/2+1 A2(A1+A3+1)/2+1;

是的话,为 A 2 − ( A 1 + A 3 ) / 2 A_2-(A_1+A_3)/2 A2(A1+A3)/2

c o d e : code: code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include<queue>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=50010;
ll num1[N],num2[N],bb[N];
int main()
{
    ll a,b,c;
    cin >> a >> b >> c;
    if(a+c>2*b)
    {
        if((a+c)%2==0)
        {
            cout << (a+c)/2-b << endl;
        }
        else
        {
            cout << (a+c+1)/2-b+1 << endl;
        }
    }
    else
    {
        cout << 2*b-a-c << endl;
    }
    return 0;
}

H.Increasing Triples

Given are three sequences of N integers each: A=(A1,…,AN),B=(B1,…,BN),C=(C1,…,CN).
You can permute each of these sequences in any way you like. Find the maximum possible number of indices i such that Ai<Bi<Ci after permuting them.

给你三个数组,让你随意排序,求最后满足 A i < B i < C i A_i<B_i<C_i Ai<Bi<Ci的最大数

Constraints
1≤N≤105
1≤Ai,Bi,Ci≤109

输入

Input is given from Standard Input in the following format:
N
A1 A2 … AN
B1 B2 … BN
C1 C2 … CN

输出

Print the answer.

样例输入

【样例1】
5
9 6 14 1 8
2 10 3 12 11
15 13 5 7 4
【样例2】
1
10
20
30
【样例3】
3
1 1 1
1 1 2
2 2 2

样例输出

【样例1】
3
【样例2】
1
【样例3】
0

开三个优先队列来模拟一遍即可。

c o d e : code: code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
#include<queue>
#include <iomanip>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=50010;
ll num1[N],num2[N],bb[N];
int main()
{
    priority_queue <int,vector<int>,greater<int> >q1,q2,q3;
    int n,ans=0;
    cin >> n;
    int x;
    for(int i=1;i<=n;i++)
    {
        cin >> x;
        q1.push(x);
    }
    for(int i=1;i<=n;i++)
    {
        cin >> x;
        q2.push(x);
    }
    for(int i=1;i<=n;i++)
    {
        cin >> x;
        q3.push(x);
    }
    while(!q1.empty())
    {
       x=q1.top();
          q1.pop();
        while(!q2.empty()&&q2.top()<=x)
        {
            q2.pop();
        }
        if(q2.empty()) break;
        x=q2.top();
        q2.pop();
        while(!q3.empty()&&q3.top()<=x)
        {
            q3.pop();
        }
        if(q3.empty()) break;
        q3.pop();
        ++ans;
    }
    cout << ans;
    return 0;
}

G.Cow IDs

Being a secret computer geek, Farmer John labels all of his cows with binary numbers. However, he is a bit superstitious, and only labels cows with binary numbers that have exactly K “1” bits (1 <= K <= 10). The leading bit of each label is always a “1” bit, of course. FJ assigns labels in increasing numeric order, starting from the smallest possible valid label – a K-bit number consisting of all “1” bits. Unfortunately, he loses track of his labeling and needs your help: please determine the Nth label he should assign (1 <= N <= 10^7).

FJ给他的奶牛用二进制进行编号,每个编号恰好包含K 个"1" (1 <= K <= 10),且必须是1开头。FJ按升序编号,第一个编号是由K个"1"组成。

请问第N(1 <= N <= 10^7)个编号是什么。

输入

* Line 1: Two space-separated integers, N and K.

输出

the Nth label he should assign

样例输入

7 3

样例输出

10110

首先我们要算出第n个编号有几个零,几个1;之后使用全排列函数就好了;

关于组合数:如果是3个1一个0的话,有 C 3 1 C_3^1 C31种,3个1 2个0的话,有 C 4 2 C_4^2 C42种 ,依此类推。

c o d e : code: code:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1000000 + 10;
char s[maxn];
int main()
{
	int n,k,m=0,lr;
	cin >> n >> k;
    lr= 1;
	for (;n>lr;++m)
    {
		n-=lr;
		lr =lr*(k+m)/(m+1);
        // cout << n << endl;
	}
	s[0]='1';
	for (int i=1; i<k+m;++i)
    {
		if (i<=m)s[i]='0';
		else s[i] = '1';
	}
	//cout << n << endl;
	//cout << s << endl;
	while(--n && next_permutation(s+1,s+k+m));
	printf("%s\n",s);
	return 0;
}

F.Overplanting

Farmer John has purchased a new machine that is capable of planting grass within any rectangular region of his farm that is “axially aligned” (i.e.,with vertical and horizontal sides). Unfortunately, the machine malfunctions one day and plants grass in not one, but N (1 <= N <= 10)different rectangular regions, some of which may even overlap.

Given the rectangular regions planted with grass, please help FJ compute the total area in his farm that is now covered with grass.

让你求多个矩阵面积的并。

输入

* Line 1: The integer N.
* Lines 2…1+N: Each line contains four space-separated integers x1 y1 x2 y2 specifying a rectangular region with upper-left corner (x1,y1) and lower-right corner (x2,y2). All coordinates are in the range -10,000…10,000.

输出

* Line 1: The total area covered by grass.

样例输入

2
0 5 4 1
2 4 6 2

样例输出

20

这里可以使用扫描线加线段树,但是我太菜了???,这里使用矩阵切割算法:

用当前矩形去切割之前所有加入至矩形集合 S 中的矩形,将它们分成更多的小矩形,并再加入集合 S 中,并删除原来在集合 S 中的矩形,最后加入当前矩形。

假设目前插入的矩形为(x3,y3,x4,y4),则我们现在要用他来切割集合内的矩形(x1,y1,x2,y2)。

我们先在x轴方向上进行线段切割

k1=max(x1,x3)
k2=min(x2,x4)

则k1,k2即为两个矩形x轴上的交点。

倘若x1<k1,说明产生了新矩形(x1,y1,k1,y2)

倘若x2>k2,说明产生了新矩形(k2,y1,x2,y2);

image-20210815091507951

再从y轴方向切割:

image-20210815091307221

之后再把新矩形加到集合里,最后扫一遍集合计算面积和就好了。

c o d e : code: code:


#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1005;
int n,tot=0;
struct node
{
	ll x1,y1,x2,y2;
}a[maxn];
void add(ll x1,ll y1,ll x2,ll y2)
{
	a[++tot]=(node){x1,y1,x2,y2};
}
void cnt(int p,ll x1,ll y1,ll x2,ll y2,int f)
{
	ll k1,k2;
	if(!f)
	{
		k1=max(x1,a[p].x1);
		k2=min(x2,a[p].x2);
		if(a[p].x1<k1) add(a[p].x1,a[p].y1,k1,a[p].y2);
		if(k2<a[p].x2) add(k2,a[p].y1,a[p].x2,a[p].y2);
		cnt(p,k1,y1,k2,y2,1);
	}
	else
	{
		k1=min(y1,a[p].y1);
		k2=max(y2,a[p].y2);
		if(k1<a[p].y1) add(x1,a[p].y1,x2,k1);
		if(k2>a[p].y2) add(x1,k2,x2,a[p].y2);
	}
}
int main()
{
    cin >> n;
    ll x1,y1,x2,y2;
    cin >> x1 >> y1 >> x2 >> y2;
	add(x1,y1,x2,y2);
	//cout << tot << endl;
	for(int i=2;i<=n;i++)
	{
		scanf("%lld %lld %lld %lld",&x1,&y1,&x2,&y2);
		for(int j=1;j<=tot;j++)
		{
			if(a[j].x1>=x2||a[j].x2<=x1||a[j].y1<=y2||a[j].y2>=y1)
		    {
			     continue;
	     	}
		cnt(j,x1,y1,x2,y2,0);
		a[j]=a[tot]; //将原来的删除,用最后一个覆盖。   
		tot--;j--;

		//cout << tot << endl;
		}
		add(x1,y1,x2,y2);
	 }
	 ll ans=0;
	// cout << tot << endl;
	 for(int i=1;i<=tot;i++)
	 {
	 	ans+=(a[i].x2-a[i].x1)*(a[i].y1-a[i].y2);
	 	//cout << ans << endl;
	 }
	 cout << ans;
	  return 0;
}

typora-user-images\image-20210815091307221.png" alt=“image-20210815091307221” style=“zoom:25%;” />

之后再把新矩形加到集合里,最后扫一遍集合计算面积和就好了。

c o d e : code: code:


#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1005;
int n,tot=0;
struct node
{
	ll x1,y1,x2,y2;
}a[maxn];
void add(ll x1,ll y1,ll x2,ll y2)
{
	a[++tot]=(node){x1,y1,x2,y2};
}
void cnt(int p,ll x1,ll y1,ll x2,ll y2,int f)
{
	ll k1,k2;
	if(!f)
	{
		k1=max(x1,a[p].x1);
		k2=min(x2,a[p].x2);
		if(a[p].x1<k1) add(a[p].x1,a[p].y1,k1,a[p].y2);
		if(k2<a[p].x2) add(k2,a[p].y1,a[p].x2,a[p].y2);
		cnt(p,k1,y1,k2,y2,1);
	}
	else
	{
		k1=min(y1,a[p].y1);
		k2=max(y2,a[p].y2);
		if(k1<a[p].y1) add(x1,a[p].y1,x2,k1);
		if(k2>a[p].y2) add(x1,k2,x2,a[p].y2);
	}
}
int main()
{
    cin >> n;
    ll x1,y1,x2,y2;
    cin >> x1 >> y1 >> x2 >> y2;
	add(x1,y1,x2,y2);
	//cout << tot << endl;
	for(int i=2;i<=n;i++)
	{
		scanf("%lld %lld %lld %lld",&x1,&y1,&x2,&y2);
		for(int j=1;j<=tot;j++)
		{
			if(a[j].x1>=x2||a[j].x2<=x1||a[j].y1<=y2||a[j].y2>=y1)
		    {
			     continue;
	     	}
		cnt(j,x1,y1,x2,y2,0);
		a[j]=a[tot]; //将原来的删除,用最后一个覆盖。   
		tot--;j--;

		//cout << tot << endl;
		}
		add(x1,y1,x2,y2);
	 }
	 ll ans=0;
	// cout << tot << endl;
	 for(int i=1;i<=tot;i++)
	 {
	 	ans+=(a[i].x2-a[i].x1)*(a[i].y1-a[i].y2);
	 	//cout << ans << endl;
	 }
	 cout << ans;
	  return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

\ 安 /

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值