2014-2015 NEERC
2015-07-20 14:46:19
【传送门】
总结:NEERC,一贯的欧洲风格。
暑假的组队训练,赛中6题,目前补到7题。更新一下有意思的题的题解。
A题:规律。先选所有奇数行进行操作就能发现规律。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; int n,m; int main(){ freopen("alter.in","r",stdin); freopen("alter.out","w",stdout); while(scanf("%d%d",&n,&m) != EOF){ printf("%d\n",n / 2 + m / 2); for(int i = 2; i <= n; i += 2){ printf("%d %d %d %d\n",i,1,i,m); } for(int i = 2; i <= m; i += 2){ printf("%d %d %d %d\n",1,i,n,i); } } return 0; }
B题:排序+贪心。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; struct node{ int g,a,b,id; double tg; }P[MAXN]; bool cmp(node A,node B){ if(A.b == 0 && B.b != 0) return true; else if(A.b != 0 && B.b == 0) return false; else if(A.b == 0 && B.b == 0){ return A.a > B.a; } else{ return (double)A.a / A.b > (double)B.a / B.b; } } bool cmp_id(node A,node B){ return A.id < B.id; } int main(){ freopen("burrito.in","r",stdin); freopen("burrito.out","w",stdout); int n,A,B; scanf("%d%d%d",&n,&A,&B); for(int i = 1; i <= n; ++i){ scanf("%d%d%d",&P[i].g,&P[i].a,&P[i].b); P[i].id = i; P[i].tg = 0.0; } sort(P + 1,P + n + 1,cmp); double tA = 0,tB = 0; for(int i = 1; i <= n; ++i){ if(1.0 * P[i].b * P[i].g + tB > B){ double cur = (1.0 * B - tB) / P[i].b; P[i].tg = cur; tB = B; tA += 1.0 * P[i].a * cur; break; } else{ P[i].tg = P[i].g; tB += 1.0 * P[i].b * P[i].g; tA += 1.0 * P[i].a * P[i].g; } } if(tA < (double)A){ printf("-1 -1\n"); return 0; } printf("%f %f\n",tA,tB); sort(P + 1,P + n + 1,cmp_id); for(int i = 1; i < n; ++i){ printf("%f ",P[i].tg); } printf("%f\n",P[n].tg); return 0; }
D题:Simpson积分
题意:将一个由圆柱体以及上下两个球型帽组成的油罐斜着放置,内部放有一定高度的液体,问液体总体积。
微分 x 坐标,根据 dx 对应的横截面积进行Simpson积分 .... 一开始用很young的暴力计算敲到死....
直到补了 BZOJ 1502 那道月下柠檬树的题才直到通过 x 算 f(x)可以暴力检索每个轮廓来求。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-4; const int INF = (1 << 30) - 1; const double PI = acos(-1.0); double d,l,r,t,h; double sina,cosa,tana,dis,C1,C2,K,B,TX,LM,RM; map<double,double> mp; bool flag; void Pre_cal(){ if(fabs(t - l) < eps){ flag = false; //非斜线 TX = h - l / 2.0; } else{ flag = true; sina = t / l; cosa = sqrt(1.0 - sina * sina); tana = sina / cosa; K = -tana; B = h / cosa - d / 2.0 + K * l / 2.0; } dis = sqrt(r * r - d * d / 4.0); C1 = -l / 2.0 + dis; C2 = l / 2.0 - dis; LM = -l / 2.0 - r + dis; RM = l / 2.0 + r - dis; } inline double Cir_cal(double a,double x){ return -sqrt(r * r - (x - a) * (x - a)); } inline double Line(double x){ if(flag) return K * x + B; else{ if(x > TX) return -INF; else return (double)INF; } } inline double Cal_area(double a,double b){ if(fabs(a + b) < eps) return PI * b * b; if(a < 0){ double ang = 2.0 * acos(a / b); double t_area = sqrt(b * b - a * a) * (-a); return PI * b * b * ang / (2.0 * PI) - t_area; } else{ double ang = 2.0 * acos(a / -b); double t_area = sqrt(b * b - a * a) * a; return PI * b * b * (2.0 * PI - ang) / (2.0 * PI) + t_area; } } double F(double x){ if(mp.find(x) != mp.end()) return mp[x]; double Y1,Y2; if(x < -l / 2.0) Y1 = Y2 = Cir_cal(C1,x); else if(x > l / 2.0) Y1 = Y2 = Cir_cal(C2,x); else Y1 = Y2 = -d / 2.0; Y1 = min(-Y2,max(Y1,Line(x))); return mp[x] = Cal_area(Y1,Y2); } inline double Simpson(double a,double b){ return (F(a) + 4.0 * F(getmid(a,b)) + F(b)) * (b - a) / 6.0; } double Integral(double tl,double tr){ double mid = getmid(tl,tr); double sum = Simpson(tl,tr); double suml = Simpson(tl,mid); double sumr = Simpson(mid,tr); if(fabs(sum - suml - sumr) < eps) return sum; else return Integral(tl,mid) + Integral(mid,tr); } void Solve(){ Pre_cal(); double ans = Integral(LM,-l / 2.0) + Integral(-l / 2.0,l / 2.0) + Integral(l / 2.0,RM); printf("%.2f\n",ans / 1e6); } int main(){ freopen("damage.in","r",stdin); freopen("damage.out","w",stdout); scanf("%lf%lf%lf%lf%lf",&d,&l,&r,&t,&h); Solve(); return 0; }
E题:构造。
题意:给出一个对手的有限状态自动机FSM,要你构造一个自己的FSM,使得自己的胜率接近100%。对手每次的起点任意,而你的FSM起点固定为1。
思路:数据范围很小,对手的FSM点数<=100,要求你构造的FSM点数<=50000。这么考虑,先让对手的FSM从1号点开始跑,然后我们构造一个对应的必胜的FSM(点和边都一一对应,也是n个点),我们称其为一个 copy,然后逐一枚举对手从2~n号点开始跑,每次我们的FSM都从1号点开始跑,如果当前需要的边存在,那么接着跑,如果不存在,那么我们构造一个新的 copy(也是n个点),让新的 copy 对于接下来的点也是全完必胜的(只用构造 n 个点,因为最多跑完 n 个点产生循环)。以此类推,最后我们的FSM应该是若干个连接在一起的 copy。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 110; const int MAXN_2 = 50010; int e[MAXN][3],e2[MAXN_2][3]; int sta[MAXN],sta2[MAXN_2]; int vis[MAXN][MAXN_2]; int tot,cnt,n; void new_copy(){ for(int i = 1; i <= n; ++i){ //建立完全映射FSM sta2[i + cnt] = (sta[i] + 1) % 3; e2[i + cnt][sta[i]] = e[i][sta2[i + cnt]] + cnt; } } void Solve(int st){ int u = st,v = 1; tot++; while(1){ if(vis[u][v] == tot) break; //检测到环 vis[u][v] = tot; int nxt_u = e[u][sta2[v]]; int &nxt_v = e2[v][sta[u]]; if(nxt_v == 0){ //没有后继点 new_copy(); e2[v][sta[u]] = nxt_u + cnt; //进入另外造的必胜copy后指向对应的点 cnt += n; } u = nxt_u; v = nxt_v; } } int main(){ freopen("epic.in","r",stdin); freopen("epic.out","w",stdout); char s[10]; char Ch[4] = "RPS"; int a,b,c; scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%s%d%d%d",s,&a,&b,&c); sta[i] = s[0] == 'R' ? 0 : (s[0] == 'P' ? 1 : 2); e[i][0] = a; e[i][1] = b; e[i][2] = c; } for(int i = 1; i <= n; ++i) Solve(i); printf("%d\n",cnt); for(int i = 1; i <= cnt; ++i){ printf("%c %d %d %d\n",Ch[sta2[i]], max(1,e2[i][0]),max(1,e2[i][1]),max(1,e2[i][2])); } return 0; }
F题:模拟题。看懂题意就能AC。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 1010; int n,m,f,q; int S[MAXN][MAXN]; ll val[MAXN][110]; ll A[110]; char str[MAXN]; int Trans(char c){ if(c >= '0' && c <= '9') return c - '0'; else return c - 'a' + 10; } int main(){ freopen("filter.in","r",stdin); freopen("filter.out","w",stdout); int a; scanf("%d%d",&m,&f); for(int i = 0; i < f; ++i){ scanf("%d",&a); A[i] = (ll)a; } memset(S,0,sizeof(S)); scanf("%d",&n); for(int i = 0; i < n; ++i){ scanf("%s",str); //16转2进制 int len = strlen(str); for(int j = 0; j < len; ++j){ int now = Trans(str[j]); //当前16进制转成的10进制 int st = 4 * j; if(now & 1) S[i][st] = 1; if(now & 2) S[i][st + 1] = 1; if(now & 4) S[i][st + 2] = 1; if(now & 8) S[i][st + 3] = 1; } } scanf("%d",&q); for(int i = 0; i < q; ++i){ scanf("%d",&a); for(int j = 0; j < f; ++j){ ll cur = ((ll)a * A[j]) % m; val[i][j] = cur; } } vector<int> ans; ans.clear(); for(int i = 0; i < n; ++i){ bool flag = false; for(int j = 0; j < q; ++j){ bool can = true; for(int k = 0; k < f; ++k){ if(!S[i][val[j][k]]){ can = false; break; } } if(can){ flag = true; break; } } if(flag) ans.PB(i); } printf("%d",(int)ans.size()); for(int i = 0; i < ans.size(); ++i) printf(" %d",ans[i]); puts(""); return 0; }
I题:DP(LIS),模型转化。
题意:给出 n (<=200000)个不同的数,数的值(1 <= val <= 200000),要求你去掉一些数,使得剩下来的数不会产生矛盾,产生矛盾的条件是,有这么连续的四个从左到右的数,x1、x2、x3,满足两个条件之一,(1)x3 < x1 < x2,(2)x2 < x1 < x3 。问最多保留多少点。
思路:可以看出是顺着x1~xn的值在数轴上不断地跳跃,不矛盾的条件是在跳跃的起点和终点之间没有已经到达过的点,所以只有两种情况,(1)不断地朝一个方向走。(2)朝一个方向走后开始不断地向内迂回。
发现:先把数进行转化一下,A[x[i]] = i,表示数 x[i] 在数列中的位置为 i ,获得一个新的数轴 。然后我们可以枚举迂回的终点,再在转化之后的数轴上找以当前枚举的迂回终点为起点,分别向左右两边走的两条最长下降子序列长度,设为 L 和 R,那么以当前枚举点为终点的时候最多保留的点就是 L+R-1 。为什么这样做可以呢,因为我们找到的子序列中的值的是比终点小的值,而这个值的含义就是在原先输入的数列中的位置,那么我们找到的数肯定比终点更早出现,那么就总可以找到一种走的方案使得产生迂回,最后到达终点。(思考)
在转化后的数轴中,我们只要正着、反着跑两边LIS,记录以每个点为起点分别向左右两边拓展能形成的最长下降子序列长度,记录在 L[] , R[] 数组中,最后枚举一下每个点为迂回终点,统计 Li+Ri-1 的最大值即可。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 200010; int A[MAXN],n; int Q[MAXN],len; int L[MAXN],R[MAXN]; int main(){ freopen("improvements.in","r",stdin); freopen("improvements.out","w",stdout); int a; scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d",&a); A[a] = i; } len = 0; for(int i = 1; i <= n; ++i){ int pos = lower_bound(Q + 1,Q + len + 1,A[i]) - Q; Q[pos] = A[i]; if(pos > len) len++; L[i] = len; } memset(Q,0,sizeof(Q)); len = 0; for(int i = n; i >= 1; --i){ int pos = lower_bound(Q + 1,Q + len + 1,A[i]) - Q; Q[pos] = A[i]; if(pos > len) len++; R[i] = len; } int ans = 0; for(int i = 1; i <= n; ++i){ ans = max(ans,L[i] + R[i] - 1); } printf("%d\n",ans); return 0; }
J题:dfs 枚举每位的数字,检索到答案即可。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int vis[105],a[105],n,len; char s[1005]; int dfs(int k,int num) { int x; if (k>=len) return 1; if (k!=len-1) { x=(s[k]-'0')*10+s[k+1]-'0'; if (vis[x]==0&&x<=n&&x>0) { a[num]=x; vis[x]=1; if (dfs(k+2,num+1)) return 1; vis[x]=0; } } x=s[k]-'0'; if (vis[x]==0&&x<=n&&x>0) { a[num]=x; vis[x]=1; if (dfs(k+1,num+1)) return 1; vis[x]=0; } return 0; } int main() { int i; freopen("joke.in","r",stdin); freopen("joke.out","w",stdout); while (~scanf("%s",s)) { memset(vis,0,sizeof(vis)); len=strlen(s); if (len<=9) n=len; else n=9+(len-9)/2; dfs(0,1); for (i=1;i<n;i++) printf("%d ",a[i]); printf("%d\n",a[n]); } }