GYM100702 D
来源:互联网 发布:适合游戏编程的电脑 编辑:程序博客网 时间:2024/06/06 18:42
题意:
有一个n个数字的可重集合,给出
n<=60
#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#include<algorithm>#define N 11000#define LL long longusing namespace std;struct node{LL d,f;}a[N],b[N],c[N],r[N];LL ans[N];int n,m,num;LL gcd(LL a,LL b){ if(b==0) return a; return gcd(b,a%b);}int find(LL t){ int l=1,r=n; while(l<=r) { int mid=(l+r)/2; if(a[mid].d==t) return mid; if(a[mid].d>t) r=mid-1; else l=mid+1; } return -1;}bool check(LL t){ for(int i=1;i<=n;i++) b[i]=c[i]=a[i],c[i].f=0; if(t>0) { for(int i=n;i>=1;i--) { if(b[i].f==0) continue; int k=find(b[i].d-t); if(k==-1 || b[i].f>b[k].f) return 0; b[k].f-=b[i].f;c[k].f+=b[i].f; } } else { for(int i=1;i<=n;i++) { if(b[i].f==0) continue; int k=find(b[i].d-t); if(k==-1 || b[i].f>b[k].f) return 0; b[k].f-=b[i].f;c[k].f+=b[i].f; } } for(int i=1;i<=n;i++) if(c[i].d==0 && c[i].f==0) return 0; return 1;}int main(){ int z,zu=0;scanf("%d",&z); while(z--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i].d); for(int i=1;i<=n;i++) scanf("%lld",&a[i].f); LL g=a[1].f; for(int i=1;i<=n;i++) g=gcd(g,a[i].f); int tmp=0,tn=n; while(g%2==0) g/=2,tmp++; for(int i=1;i<=n;i++) a[i].f/=1ll<<tmp; for(int i=1;i<=n;i++) r[i]=a[i]; num=0; while(n>1) { LL x=a[2].d-a[1].d; check(x); m=0; for(int i=1;i<=n;i++) if(c[i].f) a[++m]=c[i]; n=m; ans[++num]=x; } sort(ans+1,ans+num+1); n=tn; for(int i=1;i<=n;i++) a[i]=r[i]; for(int i=num;i>=1;i--) { if(check(-ans[i])==0) check(ans[i]); else ans[i]=-ans[i]; m=0; for(int i=1;i<=n;i++) if(c[i].f) a[++m]=c[i]; n=m; } while(tmp) ans[++num]=0,tmp--; sort(ans+1,ans+num+1); printf("Case #%d: ",++zu); for(int i=1;i<=num;i++) printf("%lld ",ans[i]); printf("\n"); } return 0;}
题解:
首先,最小的负值出现次数是
证明:
假设把所有0拿走,如果拿走了k个0,那所有值的出现次数都要除
先把所有0拿走
设最小负值和次小负值的差为d。最小负值一定是所有负数的和,次小负值要么是最小负值减最大负数,要么是最小负值加最小正数,说明d和-d一定存在一个。
然后我就不知道该拿哪一个,看了一眼题解
题解说,不管拿哪一个,本质上是没有区别的,意思就是最终拿出来的所有数的绝对值的集合是一样的。
脑补一下确实是这样的,考虑拿走一个数d之后,要把所有值分成两个集合,包含d的集合里所有的值减d后和不包含d的集合相等。
对于两个值x,y,如果|x-y|=d,就会在这两个值之间连边,然后看d的正负从小到大或从大到小扫一遍。
但是注意,d和-d的区别只是所有值都从去小的那一边还是都去大的那一边。
也就是说,选择d和-d得到的是两个平移后相等的集合,那么下一步的选择d’的绝对值是不会改变的。
于是就可以把所有数的绝对值拿出来,排序后从大到小贪心。合法的情况下做出来的值里一定会包含0。先假设当前数是负的,然后从小到大扫一遍,判合不合法就好了。
阅读全文
0 0
- GYM100702 D
- d
- d
- d
- d
- d
- D
- d
- d
- d
- d
- d
- %d
- d
- d
- d
- D
- d
- Python2.7 + selenium3.0配置
- 嵌入式安装telnet
- react native 实现ListView的局部更新
- 【MySQL】为什么不要问我DB极限QPS/TPS
- 自己动手实现HTTP协议
- GYM100702 D
- PHP常量
- POJ 1294 Not Too Convex Hull 笔记
- Can‘t connet to repository from configured remote.
- 170611 逆向-gctf的debug的writeup
- C#编写简单窗体计算器
- django+gunicorn+nginx, WebSocket无法正常连接
- 好用用的linux 监控命令
- python面向对象(初级篇)