题目链接
https://ac.nowcoder.com/acm/contest/5668
题解
A题Clam and Fish
题意:
题解:基于贪心的策略,如果有鱼的话,肯定钓鱼,因为如果制作鱼饵的话,后面还需要花时间用鱼饵捕鱼,而且也只能贡献一条鱼,所以肯定直接钓鱼比较优。之后考虑没有鱼的情况,分为有蛤蜊和无蛤蜊考虑,这里就有两种做法了。
1.当有蛤蜊时就直接制作鱼饵,否则的话就用鱼饵捕鱼,如果到最后还有x个鱼饵的话,就花费制作这x个鱼饵的一半时间x/2来捕鱼,这样钓的鱼的个数是最多的。
2.当有蛤蜊能够制作鱼饵时,需要考虑后面能否有足够的时间用鱼饵来捕鱼,如果有的话就制作鱼饵,否则的话就用之前已经制作的鱼饵来捕鱼。
做法一代码实现:
#include
using namespace std; const int N = 2e6+7; char s[N]; int main(){ int T;scanf("%d",&T); while(T--){ int n;scanf("%d",&n); scanf("%s",s); int ans=0,t=0; for(int i=0;i 0) ans++,t--; } printf("%d\n",ans+(t>0?t/2:0)); } return 0; } 做法二代码实现:
#include
using namespace std; const int N = 2e6+7; char s[N]; int sum[N]; int main(){ int T;scanf("%d",&T); while(T--){ int n;scanf("%d",&n); for(int i=0;i<=n;i++) sum[i]=0; scanf("%s",s); int ans=0,t=0; for(int i=n-1;i>=0;i--) sum[i]=sum[i+1]+(s[i] == '0' || s[i] == '1'); for(int i=0;i t) t++; else ans++,t--; } else if(t>0) ans++,t--; } printf("%d\n",ans); } return 0; } B题Classical String Problem
题意
题解:官方题解讲的比较好,其实就是用个指针来模拟就行了。
代码实现:
#include
using namespace std; const int N = 2e6+7; char s[N]; int main(){ int q,sum=0; scanf("%s%d",s,&q); int len=strlen(s); while(q--){ char opt[2]; scanf("%s",opt); if(opt[0]=='M'){ int x;scanf("%d",&x); sum+=x; } else{ int id;scanf("%d",&id);id--; int res=id+sum; while(res<0) res+=len; printf("%c\n",s[res%len]); } } return 0; } C题Operation Love
题意:给你20个点,让你判断这20个点形成的是左手掌还是右手掌。
题解:因为点是逆时针或者顺时针给的,所以我们可以通过求相邻两点的距离(边长)来判断是顺时针还是逆时针,以及左手掌还是右手掌。
代码实现:
#include
#define m_p make_pair #define p_i pair #define _for(i, a) for (register int i = 0, lennn = (a); i < lennn; ++i) #define _rep(i, a, b) for (register int i = (a), lennn = (b); i <= lennn; ++i) #define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n" #define mem(a, b) memset(a, b, sizeof(a)) #define mem0(a) memset(a, 0, sizeof(a)) #define fil(a, b) fill(a.begin(), a.end(), b); #define scl(x) scanf("%lld", &x) #define sc(x) scanf("%d", &x) #define abs(x) ((x) > 0 ? (x) : -(x)) #define PI acos(-1) #define lowbit(x) (x & (-x)) using namespace std; typedef long long LL; typedef unsigned long long ULL; const int maxn = 100005; const int maxm = 1000005; const int maxp = 30; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1000000007; const double eps = 1e-5; const double E = 2.718281828; int debug = 0; typedef struct poi { double x, y; poi() {} poi(double x, double y) : x(x), y(y) {} void inp() { scanf("%lf%lf", &x, &y); } bool operator<(poi tem) { return x < tem.x || (x == tem.x && y < tem.y); } //点排序-先排x再排y poi operator+(poi tem) { return poi(x + tem.x, y + tem.y); } //点/向量 相加 poi operator-(poi tem) { return poi(x - tem.x, y - tem.y); } poi operator*(double tem) { return poi(x * tem, y * tem); } double operator*(poi tem) { return x * tem.x + y * tem.y; } //点积 double operator^(poi tem) { return x * tem.y - y * tem.x; } //叉积 poi operator/(double tem) { return poi(x / tem, y / tem); } int dcmp(double x) { if (x < eps) return 0; else return x < 0 ? -1 : 1; } bool operator==(poi tem) { return dcmp(x - tem.x) == 0 && dcmp(y - tem.y) == 0; } } Vector; double Dot(Vector a, Vector b) { return a.x * b.x + a.y * b.y; } //点积 double Length(Vector a) { return sqrt(Dot(a, a)); } //长度 double Angle(Vector a, Vector b) { return acos(Dot(a, b) / Length(a) / Length(b)); } //夹角 double Cross(Vector a, Vector b) { return a.x * b.y - a.y * b.x; } //叉积 double Area(poi a, poi b, poi c) { return Cross(b - a, c - a) / 2; } //三点求三角形面积 Vector Rotate(Vector a, double rad) { return Vector(a.x * cos(rad), a.x * sin(rad) + a.y * cos(rad)); } //向量旋转rad弧度 Vector Normal(Vector a) { double L = Length(a); return Vector(-a.y / L, a.x / L); } //单位向量--调用前请确保A不是零向量 double sgn(double x) { return fabs(x) - eps < 0 ? 0 : (x > 0 ? 1 : -1); } Vector trunc(Vector a, double r) { //化为长度为r的向量 double L = Length(a); if (!sgn(L)) return a; r /= L; return Vector(a.x * r, a.y * r); } Vector rotleft(Vector a) { return Vector(-a.y, a.x); } //逆时针旋转90度 Vector rotright(Vector a) { return Vector(a.y, -a.x); } //顺时针旋转90度 Vector rotate(Vector a, const Vector p, double rad) { // a绕着点p旋转rad弧度 Vector v = a - p; double c = cos(rad), s = sin(rad); return Vector(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c); } struct Line { poi s, e; Line() {} Line(poi &s, poi &e) : s(s), e(e) {} }; double dist(poi a, poi b) { //两点距离 return sqrt((b - a) * (b - a)); } double xmult(poi p0, poi p1, poi p2) { // p0p1 X p0p2 return (p1 - p0) ^ (p2 - p0); } bool Seg_inter_line(Line l1, Line l2) { //判断直线l1和线段l2是否相交 return sgn(xmult(l2.s, l1.s, l1.e)) * sgn(xmult(l2.e, l1.s, l1.e)) <= 0; } bool equ(double a, double b) { if(debug) cout << "fabs:" << fabs(a - b) << "\n"; return fabs(a - b) < eps; } vector a; int n = 20; poi b[4]; void init() { a.resize(n); } void getlr() { int l; _for(i, n) { if(debug) cout << dist(a[i], a[(i + n - 1) % n]) << "\n"; if (equ(dist(a[i], a[(i + n - 1) % n]), 1)) { for (l = i; equ(dist(a[l], a[(l + n - 1) % n]), 1); l += 2, l %= n); l += n - 2; l %= n; _for(j, 4) b[j] = a[(l + j) % n]; if (debug) _for(j, 4) cout << b[j].x << " " << b[j].y << "\n"; return; } } } void sol() { init(); _for(i, n) scanf("%lf%lf", &a[i].x, &a[i].y); getlr(); if (dist(b[0], b[1]) > dist(b[2], b[3])) reverse(b, b + 4); if (debug) { cout << "reversed:\n"; _for(j, 4) cout << b[j].x << " " << b[j].y << "\n"; } double cr = (b[1].x - b[0].x) * (b[2].y - b[0].y) - (b[1].y - b[0].y) * (b[2].x - b[0].x); if (cr > 0) cout << "right\n"; else cout << "left\n"; } int main() { // ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); #ifdef ONLINE_JUDGE #else freopen("in.txt", "r", stdin); debug = 0; #endif int T; while (cin >> T) { _for(i, T) { sol(); } } return 0; } E题Two Matchings
题意:
题解:
不难想到最优解就是排序后相邻的差值和。次优解的解法就有点麻烦了,因为可能四个或者六个形成次优解,所以这个时候就需要递推的去求1到n的次优解,即dp。
定义getV4(i)表示i到i+3的次优解,getV6(i)表示i到i+3的次优解,a数组为排序后的数组。
不难得到getV4(i)表示: a[i+2]+a[i]+a[i+3]-a[i+1],getV6(i)表示: a[i+5]-a[i]+a[i+2]-a[i+1]+a[i+4]-a[i+3]。
状态:dp[i]表示从1到i的次优解。
状态转移方程:dp[i]=min(dp[i-3]+getV4(i-3),dp[i-5]+getV6(i-5)) {i>=9&&i&1)
即从1到i的次优解由 第1到i-3的次优解 + i-3到i的次优解 和 第1到i-5的次优解 + i-5到i的次优解 的最小值(最小次优解)形成。
边界条件:dp[3]=getV4(0),dp[5]=getV6(0),dp[7]=getV4(0)+getV4(4)。
详细证明和分析过程可以参考队友的博客:
代码实现:
#include
#define ll long long using namespace std; const int N = 2e5+7; ll dp[N],a[N]; ll getV4(int id){ return a[id+2]-a[id]+a[id+3]-a[id+1]; } ll getV6(int id){ return a[id+5]-a[id]+a[id+2]-a[id+1]+a[id+4]-a[id+3]; } int main(){ cin.sync_with_stdio(false); int T;cin>>T; while(T--){ int n;cin>>n; for(int i=0;i >a[i]; sort(a,a+n); ll ans=0; for(int i=0;i F题Fraction Construction Problem
题意:给你两个整数a和b,让你求出符合条件的c,d,e,f。
题解:
当a和b不互质的时候,不难找到一组符合的解,定义g为a和b的最大公约数,答案为2*(a/g),b/g,a/g和b/g。
否则的话需要分为两种情况来考虑,b有小于等于一个相异质因子和b有大于一个相异质因子。
当b有小于等于一个相异质因子时(即b为1,质数,或者平方数,立方数这一类数时),答案是无解的。
证明过程如下图:
可以找到一组d和f,使得d*f=b且d和f互质,这样d和f就从未知量变成已知量了。
根据扩展欧几里得算法,已知a,b,我们可以求出满足的x1和x2。
因此问题就转换成了,已知d和f,我们可以求出 满足 的e和c。
再把e和c同时扩大a倍,这样答案就求出来了。
因为求出的d和f不一定对应方程中真正的d和f,因此我们如果求出来的c>0,那么c表示真正的c,否则的话表示的是真正的e。
至于怎么想到这样求得呢?还是通过观察上面这个式子发现扩展欧几里得可以解决这样的问题(只要d和f确定),然后就不停往扩展欧几里得算法上凑就行了,
虽然我没想到。代码实现:
#include
#define ll long long using namespace std; struct Prime { vector arr; int vis[2000006]; void doit(int maxnum) { for(int i = 2; i <= maxnum; ++i) { if(!vis[i]) arr.push_back(i); int len=arr.size(); for(int j = 0; j < len && arr[j] * i <= maxnum; ++j) { vis[arr[j] * i] = 1; if(i % arr[j] == 0) break; } } } }P; int quick_pow(int a,int b){ int res=1; while(b){ if(b&1) res=(a*res); b>>=1; a=(a*a); } return res; } ll ex_gcd(int a,int b,ll &x1,ll &y1) { if(!b) { x1=1,y1=0; return a; } ll x2,y2; ll d=ex_gcd(b,a%b,x2,y2); x1=y2,y1=x2-(a/b)*y2; return d; } int main(){ P.doit(2000000); cin.sync_with_stdio(false); int T;cin>>T; while(T--){ int a,b,d,f;cin>>a>>b; int g=__gcd(a,b); if(g!=1){ cout<<2*(a/g)<<" "<0) cout< G题Operating on a Graph
题意:
题解:不难发现一个点至多被侵略一次后,颜色就变成了和相邻点相同的颜色。
我们可以用G[i]表示这个颜色尚未侵略过的相邻点,对于每次操作的颜色,暴力的去遍历那些尚未侵略过的点,然后更新G[i]数组就行了,同时用并查集维护属于每个点的颜色。维护G[i]数组需要用链表O(1)合并,或者启发式合并。
启发式合并代码实现:
#include
using namespace std; const int N = 8e5+7; vector G[N]; int fa[N]; int find(int x){ if(x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; } int n,m; void init(){ for(int i=0;i<=n;i++){ fa[i]=i; G[i].clear(); } } int main(){ cin.sync_with_stdio(false); int T;cin>>T; while(T--){ cin>>n>>m; init(); while(m--){ int u,v;cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } int q;cin>>q; while(q--){ int u;cin>>u; if(fa[u]!=u) continue; vector p=G[u]; G[u].clear(); int len=p.size(); for(int i=0;i list模拟链表合并代码实现:
#include
using namespace std; const int N = 8e5+7; list G[N]; int fa[N]; int find(int x){ if(x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; } int n,m; void init(){ for(int i=0;i<=n;i++){ fa[i]=i; G[i].clear(); } } int main(){ cin.sync_with_stdio(false); int T;cin>>T; while(T--){ cin>>n>>m; init(); while(m--){ int u,v;cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } int q;cin>>q; while(q--){ int u;cin>>u; if(fa[u]!=u) continue; list p=G[u]; G[u].clear(); for(list ::iterator it=p.begin();it!=p.end();it++){ int v=find(*it); if(v!=u){ fa[v]=u; G[u].splice(G[u].end(),G[v]); } } } for(int i=0;i L题Problem L is the Only Lovely Problem
签到题,不多说。