《牛客练习赛65》

C:

显然步数只存在0,1,2,3,-1的情况。随便画画就能明白.
对于0的情况,显然是(0,0).
对于1的情况,显然为该点在(0,0)和某点的连线上。这里可以用斜率来判断。

还有就是如果斜率的个数 <= 1,说明无法完成,这里需要在下面的情况之前特判。
对于2的情况。当n > 3时。都可以2步。

对于n == 2的情况。
如果(0,0)和两点中的某一点的连边和另外一个和查询点的连边都平行。
说明不能两步。(这时也可以发现,这时四点成平行四边形).那么就是3步。
事实上就是因为三点以上不可能形成平行四边形,所以肯定满足2步.
否则就是2步。
其他的就是-1了。
主要还是多画画试试.

这里几个细节。

pii存斜率-4/1.和4/-1最后的结果一样。

因为他们的gcd是-1,1.

即当两个数正负不同时,gcd会根据前面那个来取符号。

那么显然/gcd之后都是4/-1.

然后mp[pii{}] != 0,是会增加元素的!!!.

要用count来判断。还有就是输入的时候过滤(0,0)点。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5+5;
const int M = 1e6+5;
const int Mod = 998244353;
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define rg register
#define pb(a)  push_back(a)
#define mk(a,b) make_pair(a,b)
#define dbg(x) cout << "now this num is " << x << endl;
#define met0(axx) memset(axx,0,sizeof(axx));
#define metf(axx) memset(axx,-1,sizeof(axx));
#define sd(ax) scanf("%d",&ax)
#define sld(ax) scanf("%lld",&ax)
#define sldd(ax,bx) scanf("%lld %lld",&ax,&bx)
#define sdd(ax,bx) scanf("%d %d",&ax,&bx)
#define sddd(ax,bx,cx) scanf("%d %d %d",&ax,&bx,&cx)
#define sfd(ax) scanf("%lf",&ax)
#define sfdd(ax,bx) scanf("%lf %lf",&ax,&bx)
#define pr(a) printf("%d\n",a)
#define plr(a) printf("%lld\n",a)
/*
显然步数只存在0,1,2,3,-1的情况。随便画画就能明白.
对于0的情况,显然是(0,0).
对于1的情况,显然为该点在(0,0)和某点的连线上。这里可以用斜率来判断。
对于2的情况。当n > 3时。都可以2步。
对于n == 2的情况。
如果(0,0)和两点中的某一点的连边和另外一个和查询点的连边都平行。
说明不能两步。(这时也可以发现,这时四点成平行四边形).那么就是3步。
事实上就是因为三点以上不可能形成平行四边形,所以肯定满足2步.
否则就是2步。
其他的就是-1了。
主要还是多画画试试.
*/
int a[N],b[N];
double eps = 1e-13; 
bool cal(int x1,int y1,int x2,int y2)
{
    if(y1*x2 == y2*x1) return 1;
    else return 0;
}
void run()
{
    int n,q;sdd(n,q);
    int cnt = 0,num = 0;
    map<pii,int> mp;//正,正/负
    for(int i = 1;i <= n;++i)
    {
        int x,y;sdd(x,y);
        if(x == 0 && y == 0) continue;
        int t = __gcd(x,y);
        a[++cnt] = x,b[cnt] = y;
        if(mp[pii{x/t,y/t}] == 0) num++;
        mp[pii{x/t,y/t}]++;
    }
    while(q--)
    {
        int x,y;sdd(x,y);
        if(x == 0 && y == 0) printf("0\n");
        else
        {
            int t = __gcd(x,y);
            if(mp.count(pii{x/t,y/t}) != 0) printf("1\n");
            else if(mp.size() <= 1) printf("-1\n");
            else if(cnt > 2) printf("2\n");
            else if(cnt == 2)
            {
                if(cal(a[1],b[1],a[2]-x,b[2]-y) && cal(a[2],b[2],a[1]-x,b[1]-y)) printf("3\n");
                else printf("2\n");
               // printf("here\n");
            }
        }
    }
}  
int main()
{
    run();
  //system("pause");
    return 0;
}

 

D:

首先可以发现。

对如果是质数的话,显然和其他的质数组成的lcm都是唯一的。即不会重叠..

然后根据唯一分解定理。

一个数n,可以分解成若干个质数pi^kI的和.

那么很显然,如果一个数可以分解。那么就分解它。这样的价值可以最大化.

所以可以发现,最后的集合一定是一个素数幂次组成的集合.

那么如果进行统计。

背包dp转移。

因为这里需要比较大小。而背包又取模了。

所以这里用了pair来背包。first存log后的背包值。利用log来比较大小。

注意的是。对于一个质数,它的各个幂次加入之后。

和前面的产生lcm都不会自身的幂次和前面产生的lcm会重叠。因为显然lcm会随着幂次的增加而扩大,而且和质数去lcm,这个lcm又无法缩小。

所以可以证明不会和自身的幂次重叠。

然后显然从自身的小的幂次开始加入更好。因为产生的贡献一样,代价却更小。

所以显然对于自身幂次加入的数。应该是从小到大连续加入。

所以这里背包对于每个质数的幂次,从1次+2次+3次这样的思路不断更新背包。

然后这个贡献。

显然对于每个质数。可以和前面的背包dp[j-t]的每个数都产生不同的结果。

显然这个结果weidp[j-t]*k.

但需要注意的是,这里是乘。和加的背包有些不一样。因为乘的话算的都是当前加入的这个数和前面的贡献。但之前dp[j-t]的贡献就会被过滤掉。

所以应该加上之前的。即dp[j-t] + dp[j-t]*k = dp[j-t]*(k+1);

注意边界。log = 0,dp[0] = 1.

然后这里是一维的背包,所以倒着递推。消除后效性。

然后这里埃氏筛,先筛素数

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5+5;
const int M = 1e6+5;
const int Mod = 1e9+7;
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define rg register
#define pb(a)  push_back(a)
#define mk(a,b) make_pair(a,b)
#define dbg(x) cout << "now this num is " << x << endl;
#define met0(axx) memset(axx,0,sizeof(axx));
#define metf(axx) memset(axx,-1,sizeof(axx));
#define sd(ax) scanf("%d",&ax)
#define sld(ax) scanf("%lld",&ax)
#define sldd(ax,bx) scanf("%lld %lld",&ax,&bx)
#define sdd(ax,bx) scanf("%d %d",&ax,&bx)
#define sddd(ax,bx,cx) scanf("%d %d %d",&ax,&bx,&cx)
#define sfd(ax) scanf("%lf",&ax)
#define sfdd(ax,bx) scanf("%lf %lf",&ax,&bx)
#define pr(a) printf("%d\n",a)
#define plr(a) printf("%lld\n",a)
 
int p[N];
bool vis[N];
void init(int up)
{
    for(int i = 1;i < up;++i) vis[i] = 1;
    vis[1] = 0;int cnt = 0;
    for(int i = 2;i < up;++i)
    {
        if(vis[i]) 
        {
            p[++cnt] = i;
            for(int j = i+i;j < up;j += i) vis[j] = 0;
        }
    }
}
pair<double,LL> dp[N];
int main()
{
    init(N);
    int n;sd(n);
    LL sum=0;
    pair<double,LL> ans;
    ans.first = -1,ans.second = 0;
    dp[0].first = 0,dp[0].second = 1;
    for(int i = 1;;++i)
    {
        for(int j = n;j >= 1;--j)
        {
            LL t = p[i],tmp = p[i];
            for(int k = 1;t <= j;++k,tmp *= p[i],t += tmp)//tmp即该质数的各幂次。t一直加,因为显然是连续的幂次放入.
            {
                pair<double,LL> ma = make_pair(dp[j-t].first+log2(k+1),dp[j-t].second*(k+1)%Mod);
                dp[j] = max(dp[j],ma);//先比较first即log2,然后比较second。保证背包更大。
            }
        }
        sum += p[i];//当每个质数都加入一次后>n,说明不可能有更优的了。就退出。
        if(sum > n) break;
    }

    for(int i = 0;i <= n;++i) ans = max(ans,dp[i]);
    printf("%lld\n",ans.second);
    //system("pause");
    return 0;
}

 

E:

最小费用最大流。

这里的思路很巧妙。

因为要最小化xi-yi的路径权值.

所以用费用流来代替。

对每个x-y之前建边。且边的流量不断+bi.

这里就能保证了第二次走这两个点时,会走cost+b[i]*(k-1).

但显然这样看,最小的cost+0并不会在走一次后改变,所以这里又有了最大流的思路。

因为最大流的容量限制。在走了q后。会使q无法再走。所以就会到cost+b[i]*(k-1).

所以这样就能得到最小的总费用。

建图。

每对xi-yi之前建q条边(因为有q次询问),权值不断+b[i].

每条边的费用即为权值。容量为1.走一次即-1.

Code:咕咕咕.

posted @ 2020-06-13 23:13  levill  阅读(196)  评论(0编辑  收藏  举报