2016 杭州区域赛补题
A - ArcSoft's Office Rearrangement
题意:现在有n个办公区,每个办公区内有a[i]个办公人员,现在要将这些人均匀的分布在m个办公区内,每个办公区人员必须等价。现在有两个操作,1操作,可以将相邻的两个区域的人员结合在一起,2操作可以将一个办公区的人员,随意划分成两部分,两部分不一定平均,问最少需要多少次操作。
做法:贪心,从前向后枚举,不可以的情况就是不能平均的,可以预处理出来平均值,对于可以的情况,如果比平均值小,就直接加在后一个办公区内,要是与平均值一样,就可以直接跳过,要是大于平均值,就先划分成平均值的若干份,最后不足一个平均值的加在后一个办公区内。
代码如下:
#include<stdio.h> #include<iostream> using namespace std; long long t; long long n , m; long long a[100005]; long long sum; long long num; int main() { scanf("%lld" , &t); for(long long cas = 1; cas<=t; cas++) { scanf("%lld%lld" , &n , &m); sum = 0; num = 0; for(long long i=1; i<=n; i++) { scanf("%lld" , &a[i]); sum += a[i]; } if(sum % m != 0) { printf("Case #%lld: -1\n" , cas); } else { long long tmp = sum/m; // printf("%lld..\n" , tmp); for(long long i=1; i<=n; i++) { // printf("%lld...%lld..\n" , i , a[i]); if(a[i] > tmp) { num += (a[i]/tmp)-1; if(a[i]%tmp != 0) { num ++; //先剥离下来 num++; //再安装到下一个块上去 a[i+1] += a[i]%tmp; } } else if(a[i] == tmp) { continue; } else if(a[i]<tmp && a[i]!=0) { num++; a[i+1] += a[i]; } } printf("Case #%lld: %lld\n" , cas , num); } } return 0; }
F - Four Operations
题意:给你一个字符串,5<=len<=20,字符串由1~9的数字组成,问你向里面按顺序添加+ - * / 构成的算术运算式子结果最大是多少
做法:我们知道肯定让x+y尽可能的大,a*b/c的结果尽可能小,于是a*b的结果尽可能小,c尽可能大。
于是我们枚举“/”的位置,后面的作为c,a,b都只占一位 前面的作为x+y,一直x+y的长度之后,例如12121,那么和要尽可能大,要么就是1+2121,要么就是1212+1,因为这样可以保证尽可能长的长度,为什么要枚举除法的位置,而不是直接取最后一位,给前面省长度呢,因为可能a*b很大,c很小,导致我们减去的东西很大,比一位所带来的影响还要大,例如224591,所以需要枚举"/"的位置
代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; int t; char s[25]; int len; long long sum[25]; long long maxx; long long num(int l , int r) { long long res = 0; for(int i=l; i<=r; i++) { res = res*10+(s[i]-'0'); } return res; } int main() { scanf("%d" , &t); for(int cas=1; cas<=t; cas++) { scanf("%s" , s); len = strlen(s); maxx = -100000000000000000; // printf("%lld..\n" , maxx); for(int i=0; i<len; i++) { // sum[i] =0; // for(int j=0; j<i; j++) // { // sum[i] = max(sum[i] , num(0,j)+num(j+1,i)); // } sum[i] = max(num(1,i)+(s[0]-'0') , num(0,i-1)+(s[i]-'0')); // printf("%d..%lld..\n" , i , sum[i]); } for(int i=4; i<len; i++) { maxx = max(maxx , sum[i-3]-((s[i-2]-'0')*(s[i-1]-'0')/num(i,len-1))); // printf("%d...%lld...\n" , i , maxx); } printf("Case #%d: %lld\n" , cas , maxx); } return 0; } /* 100 99999999999999999999 1224591 224591 */
C - Car
题意:现在一个人驾车行驶,保证全程不减速,现在给出了n个测速点,保证测速点一定是在整秒的时候测量的,问这个人通过这n个测速点最快的时间
做法:正向的,全程不减速不好写,对于每一段,你无法确定速度与时间,如果这一段比上一段长,你就直接设置成一秒,那万一下一段很短,岂不是前面的设置都错了;
于是我们反向想,倒过来就是全程不加速,那么最后一段一定是一秒,速度最大,这样可以保证全程时间尽可能的短,于是最后一段的时间与速度都是知道的,那么前一段的速度要小于这一段,时间要尽可能的短,于是整除向上取整即可,记得更新速度,即可解决
坑点:不能直接计算速度v 精度卡的非常的死 必须保存距离与时间
代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<math.h> #include<string.h> using namespace std; int t; int n; long long b[100005]; long long a[100005]; //long long num1 , num2; void debug() { for(int i=1; i<=n; i++) { printf("%d..%lld...\n" ,i , a[i]); } } int main() { scanf("%d" , &t); for(int cas=1; cas<=t; cas++) { scanf("%d" , &n); // num1 = 0; b[0] = 0; for(int i=1; i<=n; i++) { scanf("%lld" , &b[i]); // a[i] = num2-num1; // num1 = num2; } sort(b+1 , b+1+n); for(int i=1; i<=n; i++) { a[i] = b[i]-b[i-1]; } // debug(); long long ans = 1; // double v = a[n]*1.00; long long tmp; tmp = 1; for(int i=n-1; i>=1; i--) { tmp = ceil((1.00*a[i]*tmp)/a[i+1]); // printf("%d..%lld..\n" , i , tmp); ans += tmp; } printf("Case #%d: %lld\n" , cas , ans); } return 0; }
B - Bomb
题意:现在有一对炸弹,要全部都引爆,每个炸弹有自己的圆心和半径,引爆之后在范围内的炸弹都会被引爆,问引爆所有炸弹所需要的最小的花费。n<=1e3
做法:刚开始的做法是,首先,我们可以根据引爆范围n^2的建图,有向图,入度为零的是一定要点的,它所能到达的点全部都删去, 剩下的就是在连通块中找最小花费的点了。剩下的这部分我是直接联通快中招最小点的。 这样是一定不对的,例如,1-2 2-3 3-1 2-4 4的值最小,但是引爆4并不能引爆1,2,3. 于是,只有在同一个强连通分量里面才可以求最小值,剩下的就是入度为零的全部引爆了 于是,先缩点,形成DAG之后求 Tarjan的时间复杂度O(n+m) 缩点时间复杂度O(n^2) 求解O(n)
代码如下:
/* 刚开始的做法是:首先,我们可以根据引爆范围n^2的建图,有向图,入度为零的是一定要点的,它所能到达的点全部都删去, 剩下的就是在连通块中找最小花费的点了。剩下的这部分我是直接联通快中招最小点的。 这样是一定不对的,例如,1-2 2-3 3-1 2-4 4的值最小,但是引爆4并不能引爆1,2,3. 于是,只有在同一个强连通分量里面才可以求最小值,剩下的就是入度为零的全部引爆了 于是,先缩点,形成DAG之后求 Tarjan的时间复杂度O(n+m) 缩点时间复杂度O(n^2) 求解O(n) */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<math.h> using namespace std; const int maxn = 1003; int t; int n; struct CIRCLE { double x,y; double r; int c; }circle[maxn]; double dis(int i , int j) { return sqrt((circle[i].x-circle[j].x)*(circle[i].x-circle[j].x)+(circle[i].y-circle[j].y)*(circle[i].y-circle[j].y)); } void input() { for(int i=1; i<=n; i++) { scanf("%lf%lf%lf%d" , &circle[i].x , &circle[i].y , &circle[i].r , &circle[i].c); } } struct EDGE { int to , next; }edge[maxn*maxn]; int head[maxn] , tot; //int head2[maxn] , tot2; int ans; int du[maxn]; int low[maxn] , dfn[maxn] , Stack[maxn] , num[maxn];///num记录缩点之后每个点的大小 int belong[maxn]; int index , scc , top; bool InStack[maxn]; void init() { memset(head , -1 , sizeof(head)); tot = 0; // memset(head2 , -1 , sizeof(head2)); // tot2 = 0; ans = 0; for(int i=1; i<=n; i++) { num[i] = 10004; } memset(du , 0 , sizeof(du)); } void add(int u , int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } //void add2(int u , int v) //{ // edge2[tot2].to = v; // edge2[tot2].next = head2[u]; // head2[u] = tot2++; //} void build() { for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(j == i) continue; if(dis(i , j) <= circle[i].r) { add(i , j); } } } } void Tarjan(int u) { int v; low[u] = dfn[u] = ++index; Stack[top++] = u; InStack[u] = true; for(int i=head[u]; i!=-1; i=edge[i].next) { v = edge[i].to; if( !dfn[v] ) { Tarjan(v); if( low[u] > low[v] ) { low[u] = low[v]; } } else if(InStack[v] && low[u] > dfn[v]) { low[u] = dfn[v]; } } if(low[u] == dfn[u]) { scc++; do { v = Stack[--top]; InStack[v] = false; belong[v] = scc; num[scc] = min(num[scc] , circle[v].c); }while(v!=u); } } void solve() { memset(dfn , 0 , sizeof(dfn)); memset(InStack , false , sizeof(InStack)); index = scc = top = 0; for(int i=1; i<=n; i++) { if( !dfn[i] ) { Tarjan(i); } } for(int i=1; i<=n; i++) { for(int j=head[i]; j!=-1; j=edge[j].next) { int tmp = edge[j].to; if(belong[i] != belong[tmp]) { // add2(belong[i] , belong[tmp]); du[belong[tmp]]++; } } } } int main() { scanf("%d" , &t); for(int cas=1; cas<=t; cas++) { scanf("%d" , &n); init(); input(); build(); solve(); for(int i=1; i<=scc; i++) { if(du[i] == 0) { ans += num[i]; } } printf("Case #%d: %d\n" , cas , ans); } return 0; }
题意:现在定义一个操作,对于正整数,强调一下,是正整数,数字y的每一位的K次方-y=x,现在给定了x与k,求有多少种满足条件的y
做法:x = F(y,k)-y
因为 y > 0
所以 x > F(y,k)
因为 题目给定 x > 0
所以 F(y,k) > y
所以x > F(y,k) > y
所以y的长度不超过x的长度10位,将y分成两部分,低位部分a和高位部分b
设 F(Y,K) = F(a,k) +F(b,k);
x = F(a,k) +F(b,k)-a- b*1e5;
x-F(a,k)+a = F(b,k)-b*1e5
y 的个数就是使上式成立的数字的个数
于是枚举等式前边的数字,时间复杂度O(100000),预处理出来等式后边的,排序之后二分查找使之成立的个数即可
感谢:https://blog.csdn.net/luricheng/article/details/70176894提供思路与坑点
代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<math.h> using namespace std; const long long maxn = 100050; long long F[maxn]; long long t; long long x , k; long long ans[maxn]; long long poww(long long n ,long long m) { long long res = 1; for(long long i=1; i<=m; i++) { res *= n; } return res; } void init() { memset(F , 0 , sizeof(F)); for(long long i=0; i<100000; i++) { long long j = i; while( j ) { F[i] += poww(j%10 , k); j /= 10; } ans[i] = F[i]-(i*100000); } sort(ans , ans+100000); } void debug() { for(long long i=0; i<=100; i++) { printf("%lld...%lld...%lld\n" , i , F[i] , ans[i]); } } long long sea(long long xx) { long long L , R; L = lower_bound(ans , ans+100000 , xx)-ans; if(ans[L] != xx) return 0; R = upper_bound(ans , ans+100000 , xx)-ans; R--; if(ans[R] != xx) return 0; return R-L+1; } long long solve() { long long res; res = 0; for(long long i=0; i<100000; i++) { long long tmp = sea(x-F[i]+i); res += tmp; } return res; } int main() { scanf("%lld" ,&t); for(long long cas=1; cas<=t; cas++) { scanf("%lld%lld" , &x , &k); init(); // debug(); long long tmp = solve(); if(x == 0) tmp--; printf("Case #%lld: %lld\n" , cas , tmp); } return 0; }
题意:现在你有一些数字卡片,每个数字卡片上写有1~9中的一个,给你的是每一个数字有多少张卡片,问你能够组成多少种不一样的等式。
A+B=C 与 B+A=C 定义为不一样的
思路:感谢https://www.cnblogs.com/jihe/p/6024135.html不然这个bug我能de一年
直接暴力哪几个被选了就好,但是不能这样暴力,就是你现在选的是第x个,下一个选第几个,因为这样只让我们的暴力数量变多,我们其实想要的就是这个有没有被选就好了
那么就从第一个开始,看他作为第i个的时候,下一个作为第i+1个,与此同时,下一层就是我不作为第i层,i+1作为第i层,这样可以覆盖所有的情况
代码如下:
/* 错因: 首先不剪枝会WA 其次,问的是会出现多少种不一样的,而不是多少个 多少种的话,就可以整一个vis数组,最大容量是2,也就是2+3=5与3+2=5不一样 但是容量是2的时候和直接保存两个有区别吗? 试一下 */ #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; struct NODE { int a , b , c; } node[40]; int t; int maxx; int a[15]; int rem; int flag; void pre() { int tot = 0; for(int i=1; i<9; i++) { for(int j=1; i+j<=9; j++) { node[tot].a = i; node[tot].b = j; node[tot].c = i+j; // printf("%d..%d..%d..%d..\n" , tot , i , j , i+j); tot++; } } } void init() { maxx = 0; flag = 1; rem = 0; } void input() { for(int i=1; i<=9; i++) { scanf("%d" , &a[i]); a[i] = min(a[i] , 17-i); rem += a[i]; if(a[i] < 17-i) flag = 0; } } bool ok(int xx) { if(a[node[xx].a]>=0 && a[node[xx].b]>=0 && a[node[xx].c]>=0) return true; return false; } void dfs(int x , int dep , int sheng) { if(36-x+dep<=maxx) ///后面的全都可以也不行的话 就直接跳出 return ; if(dep >= 36) return ; a[node[x].a] -= 1; a[node[x].b] -= 1; a[node[x].c] -= 1; if(ok(x)) { maxx = max(maxx , dep+1); dfs(x+1 , dep+1 , sheng-3); } a[node[x].a] += 1; a[node[x].b] += 1; a[node[x].c] += 1; dfs(x+1 , dep , sheng); } void solve() { dfs(0 , 0 , rem); } int main() { pre(); scanf("%d" , &t); for(int cas=1; cas<=t; cas++) { init(); input(); // printf("%d...\n" , rem); if(flag) { printf("Case #%d: 36\n" , cas); continue; } solve(); printf("Case #%d: %d\n" , cas , maxx); } return 0; }