第一题sort 太菜了并不会
考试的时候暴力写炸了
70分(TLE)思路:可以发现对一个数x而言,加上小于lowbit(x)的数,都不影响它二进制下1个数的奇偶性。
所以,用新规则比较两个数x,y,当它们二进制下1个数的奇偶性相同时,加上小于min(lowbit(x), lowbit(y))的数都不影响它们的比较结果,必须一直加到第min(lowbit(x), lowbit(y))次时,两个数在二进制下1数量的奇偶性才会变成不同。
因此把所有的+1操作直接改为加上min(lowbit(x), lowbit(y))。
+快读 70分(然而不加快读也是70分)
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int N=100007; 5 long long n,a[N]; 6 long long read() 7 { 8 char ch=getchar();int x=0,f=1; 9 while(ch<'0'||ch>'9') 10 { 11 if(ch=='-') f=-1; 12 ch=getchar(); 13 } 14 while(ch>='0'&&ch<='9') 15 { 16 x=x*10+ch-'0'; 17 ch=getchar(); 18 } 19 return x*f; 20 } 21 long long lowbit(long long x) 22 { 23 return x&(-x); 24 } 25 long long cnt(long long x) 26 { 27 long long c=0; 28 while(x) 29 { 30 x-=lowbit(x);c++; 31 } 32 return c; 33 } 34 bool comp(int x,int y) 35 { 36 while((cnt(x)&1)==(cnt(y)&1)) 37 { 38 long long c=min(lowbit(x),lowbit(y)); 39 x+=c,y+=c; 40 } 41 return cnt(x)&1; 42 } 43 int main() 44 { 45 n=read(); 46 for(int i=1;i<=n;i++) a[i]=read(); 47 sort(a+1,a+1+n,comp); 48 for(int i=n;i>=1;i--) printf("%lld ",a[i]); 49 return 0; 50 }
改这道题改了一晚上,期间翻阅了其他人的代码,发现有一份和我写的差不多(甚至可以说思路一模一样)的代码,然而它100。不知道自己哪里出了问题。
第二题study 很典型的二分答案题,然而考试的时候死活没想到。普及组难度。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=100007; long long a[N],b[N],c[N],e[N],n; long long ans; bool pd(long long x) { int cnt=0; for(int i=1;i<=n;i++) { e[i]=x*b[i]; if(e[i]<a[i]) cnt+=ceil(1.0*(a[i]-e[i])/c[i]); if(cnt>x) return 0; } return 1; } int main() { scanf("%d",&n); long long l=1,r=0,ans; for(int i=1;i<=n;i++) { scanf("%lld%lld%lld",&a[i],&b[i],&c[i]); r=max(r,a[i]); } while(l<=r) { long long mid=l+r>>1; if(pd(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%lld",ans); return 0; }
这里有一个问题(似乎),虽然a数组不会溢出int,但x*b可能会溢出,所以要开long long,否则50分。
改了半个小时的惨痛教训。
第三题highway 先来看一下大佬的题解
枚举第k条边大小
将所有边减去k和0去max
每次暴力跑最短路即可
讲题的时候证明了为什么“将所有边减去k和0去max”在k不在最短路上时不会导致答案变小。
因为我太菜了,所以先来解释一下“将所有边减去k和0去max”这个操作。我对这个操作的理解是,枚举第k条边,设这条边的边长为x。
理想状态下,最短路上>=x的有k-1条边,剩下的<=x。此时如果把所有的边减去x和0去max,那么大于x的会剩下与x的差值,小于x的都变成0,即理想状态下的不取。此时跑最短路,再把删掉的k个x加回来,正好补给k本身和大于x的边,而对于小于k的边,因为国家政策免费,所以依然为0。
但并不是所有情况都是理想状态。但若大于x的边大于k,则权值太大,大于枚举真正的第k大的边的最短路,不会取到;
若大于x的边小于k,则最后的答案为x*k,多补到因为与0取max而等于0的边上了,大于枚举真正的第k大的路的最短路,不会取到。
似乎是这么理解的。
#include<cstdio> #include<algorithm> #include<queue> #include<cstring> using namespace std; const int N=2007; const long long INF=100000000000000; struct egde{ long long next,to,v; }e[N],e2[N]; struct node{ long long pos,dis; friend bool operator <(const node x,const node y) { return x.dis>y.dis; } }; priority_queue<node>q; int n,m,k,s,t,head[N],etot; long long dis[N],ans; bool vis[N]; void adde(int x,int y,int v) { e2[++etot].to=y; e2[etot].v=v; e2[etot].next=head[x]; head[x]=etot; } long long dijkstra() { for(int i=1;i<=n;i++) { vis[i]=0; if(i!=s) dis[i]=INF; } q.push((node){s,0}); while(!q.empty()) { int u=q.top().pos;q.pop(); if(vis[u]) continue; vis[u]=1; for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(dis[v]>dis[u]+e[i].v) { dis[v]=dis[u]+e[i].v; if(!vis[v]) q.push((node){v,dis[v]}); } } } return dis[t]; } int main() { ans=INF;int x,y,v; scanf("%d%d%d%d%d",&n,&m,&k,&s,&t); for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&v); adde(x,y,v); } memcpy(e,e2,sizeof(e2)); for(int i=1;i<=m;i++) { for(int j=1;j<=m;j++) e[j].v=max(e2[j].v-e2[i].v,(long long)0); ans=min(ans,dijkstra()+e2[i].v*k); } printf("%lld",ans); return 0; }