A HDU 5950
题意:f(n)=f(n-1)+2*f(n-2)+n4,f(1)=a,f(2)=b,求f(n)。
思路:因为(n+1)4=n4+4n3+6n2+4n+1,据此构造矩阵,做矩阵快速幂。
#include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int N=7; const ll mod=2147493647; struct Mat { ll m[N][N]; }; Mat c={1,2,1,4,6,4,1, 1,0,0,0,0,0,0, 0,0,1,4,6,4,1, 0,0,0,1,3,3,1, 0,0,0,0,1,2,1, 0,0,0,0,0,1,1, 0,0,0,0,0,0,1}; Mat mul(Mat a,Mat b) { int i,j,k; Mat tmp; memset(tmp.m,0,sizeof(tmp.m)); for(i=0;i<N;i++) for(j=0;j<N;j++) for(k=0;k<N;k++) tmp.m[i][j]=(tmp.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod; return tmp; } Mat pow(Mat a,int k) { Mat ans; memset(ans.m,0,sizeof(ans.m)); for(int i=0;i<N;i++) ans.m[i][i]=1; while (k) { if (k&1) ans=mul(ans,a); a=mul(a,a); k>>=1; } return ans; } int main() { ll a,b,ans; int t,i,n,now; Mat tmp; while (scanf("%d",&t)!=EOF) { while (t--) { scanf("%d%lld%lld",&n,&a,&b); a=a%mod;b=b%mod; if (n==1) printf("%lld ",a); else if (n==2) printf("%lld ",b); else { tmp=pow(c,n-2); ans=(tmp.m[0][0]*b%mod+tmp.m[0][1]*a%mod)%mod; now=1; for (i=6;i>=2;i--) { ans=(ans+tmp.m[0][i]*now%mod)%mod; now=now*2; }//初始 {b,a,16,8,4,2,1} printf("%lld ",ans); } } } return 0; }
(看题看到头大)
D POJ 1220
进制转化
三分
题意:有n台电脑,其中第n台电脑初始时有a[i]的电量,每分钟花费b[i]的电量。有一个充电器,每次只能给一个一台电脑充电,要保证在T时间内所有电脑在任意分钟都有电(电量不小于0),问这个充电器每分钟的充电量最小是多少。
思路:二分充电器每分钟充电量。
检验时用一个优先队列,初始时按num[i]=a[i]/b[i]从小到大排序(不充电能用的最长时间),
在0~T的每一分钟都给排在优先队列最前面的电脑充电,更新剩余电量a[i]与能用的最长时间num[i]再将其放回优先队列,
如果有某一分钟中出现给队列最前面的电脑充完电后,排在第二的电脑在前一分钟电量耗尽(num<i),则返回0。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; typedef long long ll; const int maxn=200005; const ll inf=2e12+1; struct node { ll a,b,num; bool operator < (const node &a)const{return num>a.num;} }; int n,T; node e[maxn]; int jud(ll mid) { int i; node x; priority_queue<node>q; for (i=1;i<=n;i++) q.push(e[i]); for (i=1;i<T;i++) { x=q.top(); q.pop(); x.num=x.num+(x.a+mid)/x.b; x.a=(x.a+mid)%x.b; q.push(x); if (q.top().num<i) return 0; } return 1; } ll solve(ll l,ll r) { ll mid; while (l<=r) { mid=(l+r)>>1; if (jud(mid)) r=mid-1; else l=mid+1; } return l; } int main() { int i; scanf("%d%d",&n,&T); for (i=1;i<=n;i++) scanf("%I64d",&e[i].a); for (i=1;i<=n;i++) { scanf("%I64d",&e[i].b); e[i].num=e[i].a/e[i].b; e[i].a=e[i].a%e[i].b; } ll ans=solve(0,inf); if (!jud(inf)) printf("-1 "); else printf("%I64d ",ans); return 0; }
因为要求匹配的一组数中负数要在后面,所以从后往前做。遇到确定为负数的将其压入栈中,遇到不确定的就与当前的栈顶比对,如果可以匹配就将栈顶弹出(贪心),不能匹配就可以确定这个数是负数。
#include<cstdio> #define maxn 1000005 int a[maxn],stk[maxn],top; int main() { int i,n,t,x; while (scanf("%d",&n)!=EOF) { for (i=1;i<=n;i++) scanf("%d",&a[i]); scanf("%d",&t); for (i=1;i<=t;i++) { scanf("%d",&x); a[x]=-a[x]; } for (i=n;i>=1;i--) { if (a[i]<0) stk[++top]=a[i]; else { if (a[i]+stk[top]==0) top--; else { a[i]=-a[i]; stk[++top]=a[i]; } } } if (top) printf("NO "); else { printf("YES "); for (i=1;i<=n;i++) printf("%d ",a[i]); printf(" "); } } return 0; }
使用set,如果当前set中的类型与输入类型a相同,就将b插入set;如果当前set中类型与a不同,就用lower_bound函数找出set中第一个大于等于b的数和第一个小于b的数,按照题意,比较两个数与b差的绝对值,将绝对值较小的计入答案,并在set中删除该数。
#include<cstdio> #include<set> #include<cmath> using namespace std; const int inf=0x7fffffff; const int mod=1000000; set<int>s; int main() { int a,b,n,ma,mi,now,ans; while (scanf("%d",&n)!=EOF) { s.clear(); s.insert(inf); s.insert(-inf); ans=0; while (n--) { scanf("%d%d",&a,&b); if (s.size()==2) now=a; if (a!=now) { ma=*s.lower_bound(b);//第一个大于等于b的数 mi=*--s.lower_bound(b);// 第一个小于b的数 if (b-mi<=ma-b && mi!=-inf) { ans=(ans+abs(b-mi))%mod; s.erase(mi); } else if (ma!=inf) { ans=(ans+abs(ma-b))%mod; s.erase(ma); } } else s.insert(b); } printf("%d ",ans); } return 0; }