《牛客练习赛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:咕咕咕.