【HDU5565 BestCoder Round 62 (div1)C】【优先队列做法】Clarke and baton n个人减肥m次求最后异或值
#include<stdio.h> #include<string.h> #include<ctype.h> #include<math.h> #include<iostream> #include<string> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=1e7+10,M=0,Z=1e9+7,G=78125,ms63=1061109567; int casenum,casei; int a[N]; int n,m; long long seed; int rand(int l, int r) { seed=seed*G%Z; return l+seed%(r-l+1); } struct node { int v,o; node(){} node(int v_,int o_){v=v_;o=o_;} bool operator < (const node& b)const { if(v!=b.v)return v<b.v; return o>b.o; } }; priority_queue<node>q; int main() { scanf("%d",&casenum); for(casei=1;casei<=casenum;casei++) { scanf("%d%d%lld",&n,&m,&seed); int sum=rand(m,10000000); //天啊噜,这个优化是在是太BT了,果然有时候猜极端数据特判掉是AC的好办法啊! if(m==sum) { int ans=0; for(int i=1;i<=n;i++)ans^=i; printf("%d\n",ans); continue; } for(int i=1;i<=n;i++) { a[i]=rand(0,sum/(n-i+1)); sum-=a[i]; } a[rand(1,n)]+=sum; for(int i=1;i<=n;i++)q.push(node(a[i],i)); while(m--) { int v=q.top().v; int o=q.top().o; q.pop(); q.push(node(v-1,o)); } int ans=0; while(!q.empty()) { ans^=(q.top().v+q.top().o); q.pop(); } printf("%d\n",ans); } return 0; } /* 【trick&&吐槽】 1,STL常数巨大,有可能卡常数的情况,同复杂度尽量选用替代STL的做法 2,极端数据的特判可以极大实现代码加速!不论是multiset还是priority_queue,怎么都可以在极小的时间复杂度内AC 【题意】 T(10)组数据,6s时限 对于每组数据,给你一个种子,并用种子生成n(1e7)个数a[]。 这n个数之和为sum,sum也是由种子生成的,sum的值在[m,1e7]之间, m是我们要对这n个数做减法的总次数。 具体上讲,是我们一共做m次减法,每次选取数值最小的那个做减法; 如果有多个数数值相同,那么我们选取编号最小的那个。 在所有减法操作结束之后,让你输出所有i∈[1,n]条件下的(a[i]+i)的异或和 【类型】 STL or 二分答案 or 计数排序 【分析】 方法一:STL 用STL的multiset或priority_queue维护这个操作。 虽然理论上的复杂度为O(nlogn)但是常数巨大于是TLE 方法二:二分答案 我们可以二分做完所有减法条件后的最大值,然后检测其合法性。 然后基于这个做一次线性扫描即可,时间复杂度为O(nlogn) 方法三:快排 可以通过快排来维护做完所有减法条件后的最大值。 时间复杂度也是O(nlogn) 方法四:计数排序 我们基于快排的思路,把实现改变为计数排序,时间复杂度就变为了稳定的O(n) 【时间复杂度&&优化】 200ms AC */
【HDU5565 BestCoder Round 62 (div1)C】【二分答案做法】Clarke and baton n个人减肥m次求最后异或值
#include<stdio.h> #include<string.h> #include<ctype.h> #include<math.h> #include<iostream> #include<string> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=1e7+10,M=0,Z=1e9+7,G=78125,ms63=1061109567; int casenum,casei; int a[N]; int n,m; long long seed; int rand(int l, int r) { seed=seed*G%Z; return l+seed%(r-l+1); } bool check(int v) { int tim=m; for(int i=1;i<=n;i++)if(a[i]>v) { tim-=a[i]-v; if(tim<0)return 0; } return 1; } int solve(int v) { int tim=m; for(int i=1;i<=n;i++)if(a[i]>v) { tim-=a[i]-v; a[i]=v; } int ans=0; for(int i=1;i<=n;i++) { if(tim&&a[i]==v) { --tim; --a[i]; } ans^=(a[i]+i); } return ans; } int main() { scanf("%d",&casenum); for(casei=1;casei<=casenum;casei++) { scanf("%d%d%lld",&n,&m,&seed); int sum=rand(m,10000000); //天啊噜,这个优化是在是太BT了,果然有时候猜极端数据特判掉是AC的好办法啊! if(m==sum) { int ans=0; for(int i=1;i<=n;i++)ans^=i; printf("%d\n",ans); continue; } int top=0; for(int i=1;i<=n;i++) { a[i]=rand(0,sum/(n-i+1));gmax(top,a[i]); sum-=a[i]; } int p=rand(1,n); a[p]+=sum;gmax(top,a[p]); int l=0; int r=top; while(l<r) { int mid=(l+r)>>1; if(check(mid))r=mid; else l=mid+1; } printf("%d\n",solve(l)); } return 0; } /* 【trick&&吐槽】 1,STL常数巨大,有可能卡常数的情况,同复杂度尽量选用替代STL的做法 2,极端数据的特判可以极大实现代码加速!不论是multiset还是priority_queue,怎么都可以在极小的时间复杂度内AC 【题意】 T(10)组数据,6s时限 对于每组数据,给你一个种子,并用种子生成n(1e7)个数a[]。 这n个数之和为sum,sum也是由种子生成的,sum的值在[m,1e7]之间, m是我们要对这n个数做减法的总次数。 具体上讲,是我们一共做m次减法,每次选取数值最小的那个做减法; 如果有多个数数值相同,那么我们选取编号最小的那个。 在所有减法操作结束之后,让你输出所有i∈[1,n]条件下的(a[i]+i)的异或和 【类型】 STL or 二分答案 or 计数排序 【分析】 方法一:STL 用STL的multiset或priority_queue维护这个操作。 虽然理论上的复杂度为O(nlogn)但是常数巨大于是TLE 方法二:二分答案 我们可以二分做完所有减法条件后的最大值,然后检测其合法性。 然后基于这个做一次线性扫描即可,时间复杂度为O(nlogn) 方法三:快排 可以通过快排来维护做完所有减法条件后的最大值。 时间复杂度也是O(nlogn) 方法四:计数排序 我们基于快排的思路,把实现改变为计数排序,时间复杂度就变为了稳定的O(n) 【时间复杂度&&优化】 31ms AC 没错,不要不相信你的眼睛,我真的不是输入输出数据 噗 我就是可以31ms AC这道题! 【数据】 */
【HDU5565 BestCoder Round 62 (div1)C】【计数排序做法】Clarke and baton n个人减肥m次求最后异或值
#include<stdio.h> #include<string.h> #include<ctype.h> #include<math.h> #include<iostream> #include<string> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=1e7+10,M=0,Z=1e9+7,G=78125,ms63=1061109567; int casenum,casei; int a[N],num[N]; int n,m; long long seed; int rand(int l, int r) { seed=seed*G%Z; return l+seed%(r-l+1); } int top; int solve() { int sum=0; for(int i=top;;--i) { sum+=num[i]; if(m-sum<=0) { int ans=0; for(int j=1;j<=n;++j) { if(a[j]<i)ans^=a[j]+j; else if(m){ans^=i-1+j;--m;} else ans^=i+j; } return ans; } m-=sum; } } int main() { scanf("%d",&casenum); for(casei=1;casei<=casenum;++casei) { scanf("%d%d%lld",&n,&m,&seed); int sum=rand(m,10000000); //天啊噜,这个优化是在是太BT了,果然有时候猜极端数据特判掉是AC的好办法啊! if(m==sum) { int ans=0; for(int i=1;i<=n;i++)ans^=i; printf("%d\n",ans); continue; } memset(num,0,(sum+2)*4);top=sum; for(int i=1;i<=n;++i) { a[i]=rand(0,sum/(n-i+1));++num[a[i]]; sum-=a[i]; } int p=rand(1,n); --num[a[p]]; a[p]+=sum; ++num[a[p]]; printf("%d\n",solve()); } return 0; } /* 【trick&&吐槽】 1,STL常数巨大,有可能卡常数的情况,同复杂度尽量选用替代STL的做法 2,极端数据的特判可以极大实现代码加速!不论是multiset还是priority_queue,怎么都可以在极小的时间复杂度内AC 【题意】 T(10)组数据,6s时限 对于每组数据,给你一个种子,并用种子生成n(1e7)个数a[]。 这n个数之和为sum,sum也是由种子生成的,sum的值在[m,1e7]之间, m是我们要对这n个数做减法的总次数。 具体上讲,是我们一共做m次减法,每次选取数值最小的那个做减法; 如果有多个数数值相同,那么我们选取编号最小的那个。 在所有减法操作结束之后,让你输出所有i∈[1,n]条件下的(a[i]+i)的异或和 【类型】 STL or 二分答案 or 计数排序 【分析】 方法一:STL 用STL的multiset或priority_queue维护这个操作。 虽然理论上的复杂度为O(nlogn)但是常数巨大于是TLE 方法二:二分答案 我们可以二分做完所有减法条件后的最大值,然后检测其合法性。 然后基于这个做一次线性扫描即可,时间复杂度为O(nlogn) 方法三:快排 可以通过快排来维护做完所有减法条件后的最大值。 时间复杂度也是O(nlogn) 方法四:计数排序 我们基于快排的思路,把实现改变为计数排序,时间复杂度就变为了稳定的O(n) 【时间复杂度&&优化】 100ms AC 【数据】 */