NCPC 2016:简单题解
A .Artwork
pro:给定N*M的白色格子,然后Q次黑棒,输出每次加黑棒后白色连通块的数量。(N,M<1e3, Q<1e4)
sol:倒着离线做,并查集即可。
(在线做法:https://www.cnblogs.com/asdfsag/p/10485607.html
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn = 1e6 + 10; int a[1010][1010], fa[maxn], ID[1010][1010]; struct node { int x1, y1, x2, y2; node(){} node(int x1, int y1, int x2, int y2):x1(x1), y1(y1), x2(x2), y2(y2){} }c[maxn]; int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);} int dir[][2] = {1,0,0,1,-1,0,0,-1}; stack<int>s; int main() { int n, m, q, tot = 0, x1, y1, x2, y2; scanf("%d%d%d", &n, &m, &q); for(int i = 1; i <= n; i++)for(int j = 1; j <= m; j++){ID[i][j] = ++tot; fa[tot] = tot;} for(int i = 1; i <= q; i++) { scanf("%d%d%d%d", &x1, &y1, &x2, &y2); c[i] = node(x1, y1, x2, y2); for(int x = x1; x <= x2; x++)for(int y = y1; y <= y2; y++)a[x][y]++; } int white = 0, cnt = 0; for(int i = 1; i <= n; i++)for(int j = 1; j <= m; j++)if(!a[i][j]) { white++; for(int k = 0; k < 4; k++) { int x = i + dir[k][0], y = j + dir[k][1]; if(x >= 1 && x <= n && y >= 1 && y <= m && !a[x][y]) { int u = ID[i][j], v = ID[x][y]; u = Find(u);v = Find(v); if(u != v)fa[u] = v, cnt++; } } } s.push(white - cnt); for(int i = q; i >= 2; i--) { for(int x = c[i].x1; x <= c[i].x2; x++) for(int y = c[i].y1; y <= c[i].y2; y++) { a[x][y]--; if(!a[x][y]) { white++; for(int k = 0; k < 4; k++) { int xx = x + dir[k][0], yy = y + dir[k][1]; if(xx >= 1 && xx <= n && yy >= 1 && yy <= m && !a[xx][yy]) { int u = ID[x][y], v = ID[xx][yy]; u = Find(u);v = Find(v); if(u != v)fa[u] = v, cnt++; } } } } s.push(white - cnt); } while(!s.empty()){cout<<s.top()<<endl;s.pop();} return 0; }
C .Card Hand Sorting
pro:给定N张扑克牌,保证来自一副牌,有四种花色。现在让你重排,同种花色放一起,内部递增或者递减。 重排的方式是抽出一张牌,插入到某位置。
sol:四种花色,枚举花色的排列,再枚举每种花色内是递增还是递减。 对于每种方案,ans=N-LIS。
(写个结构体还是蛮方便的。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; map<char, int>level_num, level_color; struct node { char num, color; node(){} node(char num, char color):num(num), color(color){} bool operator <(const node& now)const { if(level_color[color] == level_color[now.color])//花色相同 { //奇数递增,偶数递减 if(level_color[color] & 1)return level_num[num] < level_num[now.num]; else return level_num[num] > level_num[now.num]; } return level_color[color] < level_color[now.color]; } bool operator == (const node& now)const{return num == now.num && color == now.color;} }a[110], b[110]; int dp[60][60], n; int solve() { memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); if(a[i] == b[j])dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1); } } return n - dp[n][n]; } char s[] = "shdc"; int c[5]; int main() { int tot = 0; for(char i = '2'; i <= '9'; i++)level_num[i] = ++tot; level_num['T'] = ++tot;level_num['J'] = ++tot;level_num['Q'] = ++tot; level_num['K'] = ++tot;level_num['A'] = ++tot; cin >> n; for(int i = 1; i <= n; i++){cin >> a[i].num >> a[i].color;b[i] = a[i];} for(int i = 1; i <= 4; i++)c[i] = i; int ans = n; do { for(int i = 1; i <= 4; i++)level_color[s[i - 1]] = c[i]; for(int i = 0; i < (1 << 4); i++) { for(int j = 0; j < 4; j++) { level_color[s[j]] *= 2; if(i & (1 << j))level_color[s[j]]++; } sort(b + 1, b + 1 + n); ans = min(ans, solve()); } }while(next_permutation(c + 1, c + 5)); cout<<ans<<endl; return 0; }
D .Daydreaming Stockbroker
pro:开始你有100元,给出N天的股价单价,你每天可以选择用当时的价格交易。限制持有股份不超过100000, 问最后有多少钱。N<365;
sol:枚举之前哪天买即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; ll a[maxn],dp[maxn],ans=100; int main() { int N; scanf("%d",&N); rep(i,1,N) scanf("%lld",&a[i]); dp[1]=dp[2]=100; rep(i,2,N){ rep(j,1,i-1){ ll t=min(dp[j]/a[j],100000LL); dp[i]=max(t*a[i]+dp[j]-t*a[j],dp[i]); } ans=max(ans,dp[i]); dp[i+1]=max(dp[i+1],dp[i]); } printf("%lld\n",ans); return 0; }
E .Exponial
pro:给定N,M。求。N^((N-1)^(N-2)...)%M;(N,M<1e9)
sol:显然欧拉降幂。 注意幂>mod时才能降幂,4以内的小于1e9,所以特判。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; int n,m; int phi(int x){ int ans=x; for(int i=2;i*i<=x;i++) if(x%i==0){ ans/=i;ans*=i-1; while(x%i==0) x/=i; } //cout<<x<<" "<<ans<<" "<<endl;; if(x>1) ans/=x,ans*=x-1; return ans; } int ksm(int x,int y,int p){ int ans=1; for(;y;y>>=1){ if(y&1)ans=(ll)ans*x%p; x=(ll)x*x%p; } return ans; } int dfs(int x,int y){ if(y==1) return 0; if(x==1) return 1; if(x==5) return ksm(x,262144,y); if(x==4) return ksm(x,9,y); if(x==3) return ksm(x,2,y); if(x==2) return x%y; int ph=phi(y); return ksm(x,dfs(x-1,ph)+ph,y); } int main(){ scanf("%d%d",&n,&m); printf("%d",dfs(n,m)); return 0; }
G .Game Rank
排位模拟题。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; int n,lev=25,st,wins; char s[10010]; int main(){ scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++){ if(s[i]=='W'){ if(lev>=6&&wins>=2)st+=2,wins++; else st++,wins++; while(lev>20&&st>2)lev--,st-=2; while(lev>15&&st>3)lev--,st-=3; while(lev>10&&st>4)lev--,st-=4; while(st>5)lev--,st-=5; } else{ wins=0; if(lev<=20)st--; if(st<0){ lev++; if(lev>20)lev=20,st=0; else if(lev>15)st=2; else if(lev>10)st=3; else st=4; } } if(lev<=0)return 0*puts("Legend"); } printf("%d",lev); return 0; }
H .Highest Tower
pro:BZOJ4886
小Q正在玩一个叠塔的游戏,游戏的目标是叠出尽可能高的塔。在游戏中,一共有n张矩形卡片,其中第i张卡片的
长度为a_i,宽度为b_i。小Q需要把所有卡片按一定顺序叠成一座塔,要求对于任意一个矩形,它的长度要严格大
于它上边的任意一个矩形的长度。塔的高度为所有矩形的宽度之和。在游戏中,小Q可以将卡片翻转90度来使用,
而且必须用上全部n张卡片。请写一个程序,帮助计算小Q能叠出最高的塔的高度。
sol:定向问题。 不难证明最后是一棵或者多棵树或者环套树。 证明https://blog.csdn.net/V5ZSQ/article/details/79337446?utm_source=blogxgwz8
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000101; map<int,int>mp; int ind[maxn],tot,mx[maxn],fa[maxn],tag[maxn],val[maxn]; ll ans; int find(int x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]); } int u[maxn],v[maxn]; int main() { int N,x,y; scanf("%d",&N); rep(i,1,N){ scanf("%d%d",&u[i],&v[i]); x=u[i]; y=v[i]; if(!mp[x]) mp[x]=++tot,val[tot]=mx[tot]=x; if(!mp[y]) mp[y]=++tot,val[tot]=mx[tot]=y; x=u[i]=mp[x]; y=v[i]=mp[y]; ind[x]++; ind[y]++; } rep(i,1,tot) fa[i]=i; rep(i,1,tot){ x=find(u[i]); y=find(v[i]); if(tag[x]&&tag[y]) continue; if(x==y) tag[y]=1; else fa[x]=y,tag[y]|=tag[x],mx[y]=max(mx[x],mx[y]); } rep(i,1,tot){ ans+=1LL*(ind[i]-1)*val[i]; if(find(i)==i&&!tag[i]) ans+=mx[i]; } printf("%lld\n",ans); return 0; }
J .Jumbled Compass
模拟。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; int main() { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<=180;i++){ if((n+i)%360==m)return 0*printf("%d",i); if((n-i+360)%360==m)return 0*printf("%d",-i); } }
K .Keeping the Dogs Apart
题意: 给定两支猫的行走路线, 都是直线行走, 求他们都在走的最近距离.
思路: 模拟即可. 每次得到min{到转折点的时间}, 然后得到结束位置, 至于这个过程的最近距离,我们可以假设一个不动,那么就是点到线段的距离. 继续模拟.
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; double x[maxn][2],y[maxn][2],ans; const double inf=1e200; const double pi=4*atan(1.0); struct point{ double x,y; point(double a=0,double b=0):x(a),y(b){} }; int dcmp(double x){ return fabs(x)<0.0000000000001?0:(x<0?-1:1);} point operator +(point A,point B) { return point(A.x+B.x,A.y+B.y);} point operator -(point A,point B) { return point(A.x-B.x,A.y-B.y);} point operator *(point A,double p){ return point(A.x*p,A.y*p);} point operator /(point A,double p){ return point(A.x/p,A.y/p);} point rotate(point A,double rad){ return point(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad)); } bool operator ==(const point& a,const point& b) { return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0; } double dot(point A,point B){ return A.x*B.x+A.y*B.y;} double det(point A,point B){ return A.x*B.y-A.y*B.x;} double dot(point O,point A,point B){ return dot(A-O,B-O);} double det(point O,point A,point B){ return det(A-O,B-O);} double length(point A){ return sqrt(dot(A,A));} double angle(point A,point B){ return acos(dot(A,B)/length(A)/length(B));} bool isPointOnSegment(point p,point a1,point a2) { //点是否在线段上 return dcmp(det(a1-p,a2-p)==0&&dcmp(dot(a1-p,a2-p))<=0); } double distoseg(point P,point A,point B) { if(isPointOnSegment(P,A,B)) return 0.0; //点到线段距离 if(A==B) return length(P-A); point v1=B-A,v2=P-A,v3=P-B; if(dcmp(dot(v1,v2))<0) return length(v2); else if(dcmp(dot(v1,v3))>0) return length(v3); return fabs(det(v1,v2)/length(v1)); } double dist(double x1,double y1,double x2,double y2) { return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } void solve(int A,int B,double t) { double dx[2],dy[2]; double d1=dist(x[A][0],y[A][0],x[A+1][0],y[A+1][0]); double d2=dist(x[B][1],y[B][1],x[B+1][1],y[B+1][1]); dx[0]=(x[A+1][0]-x[A][0])/d1; dy[0]=(y[A+1][0]-y[A][0])/d1; dx[1]=(x[B+1][1]-x[B][1])/d2; dy[1]=(y[B+1][1]-y[B][1])/d2; double sx=x[A][0],sy=y[A][0],tx=x[A][0]+(dx[0]-dx[1])*t,ty=y[A][0]+(dy[0]-dy[1])*t; double res=distoseg(point(x[B][1],y[B][1]),point(sx,sy),point(tx,ty)); ans=min(ans,res); x[A][0]+=dx[0]*t; y[A][0]+=dy[0]*t; x[B][1]+=dx[1]*t; y[B][1]+=dy[1]*t; } int main() { int N,M; scanf("%d",&N); rep(i,1,N) scanf("%lf%lf",&x[i][0],&y[i][0]); scanf("%d",&M); rep(i,1,M) scanf("%lf%lf",&x[i][1],&y[i][1]); ans=110000000000; int A=1,B=1; while(A<N&&B<M){ double da=dist(x[A][0],y[A][0],x[A+1][0],y[A+1][0]); double db=dist(x[B][1],y[B][1],x[B+1][1],y[B+1][1]); if(da<db){ solve(A,B,da); A++; } else if(da==db){ solve(A,B,da); A++; B++; } else { solve(A,B,db); B++; } } printf("%.8lf\n",ans); return 0; }
It is your time to fight!