C.Insertion Sort
题意:Q次询问,每次给出N,M,Mod,问你有多少种排列,满足前面M个数字排序之后整个序列的LIS>=N-1。
思路:我们把数字看成[1,M],[N-M+1,N]两个部分,假设是A和B。分几种情况即可。
我发现我好像想错了。 https://blog.csdn.net/qq_23502651/article/details/84680151
另外:长度为N的排列的LIS为N-1的个数=(N-1)^2; 可以推出这个公式。F(i)=F(i-1)+i-1; F(2)=1; F(3)=F(2)+2...就有了.
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=110; int main() { ll T,N,M,C=0,Mod,ans; scanf("%lld",&T); while(T--){ scanf("%lld%lld%lld",&N,&M,&Mod); if(M>=N-1){ ans=1; rep(i,1,N) ans=(ll)ans*i%Mod; } else { ans=1; ll t=((N-M)*(N-M-1)+M*(N-M)+1)%Mod; rep(i,1,M) ans=(ll)ans*i%Mod; ans=(ll)ans*t%Mod; } printf("Case #%lld: %lld ",++C,ans); } return 0; }
E .The Kouga Ninja Scrolls
题意:二维平面上有一些点,现在又几种操作,修改点的位置,修改点的颜色。 询问区间异色的点在最远曼哈顿距离。
思路:最远曼哈顿距离,想到转化为切比雪夫距离,然后线段树。以为要维护最大次大,最小次小,x,y; 感觉太多太长没敢写。
我们可以不转化为切比雪夫,直接用之前的k维最远距离,建立2^k棵线段树。 这样的好处是。 一个区间的最小值,等效与补值对应的线段树的最大值的相反数。
这样我们就没必要维护最小次小了,代码会好看一点。
本题收获:最大和最小值,我们可以统一; 在合并的情况比较多的时候,开一些全局变量,然后减少情况讨论。 比如query的时候,直接与全局变量的res更新答案。
好麻烦啊。。。估计转化维切比雪夫的代码会很长。
#include<bits/stdc++.h> #define ll long long #define f first #define s second #define pii pair<ll,ll> #define rep(i,w,v) for(int i=w;i<=v;i++) using namespace std; const int maxn=200010; const ll inf=(1LL<<55); ll x[maxn],y[maxn],a[maxn][4];int c[maxn]; pii res[4][2]; struct in{ pii Mx[maxn<<2][2]; int idx; void pushup(int Now) { if(Mx[Now<<1][0].s!=Mx[Now<<1|1][0].s){ if(Mx[Now<<1][0].f>Mx[Now<<1|1][0].f){ Mx[Now][0]=Mx[Now<<1][0]; Mx[Now][1]=max(Mx[Now<<1][1],Mx[Now<<1|1][0]); } else { Mx[Now][0]=Mx[Now<<1|1][0]; Mx[Now][1]=max(Mx[Now<<1|1][1],Mx[Now<<1][0]); } } else { rep(i,0,1) Mx[Now][i]=max(Mx[Now<<1][i],Mx[Now<<1|1][i]); } } void build(int Now,int L,int R) { if(L==R){ Mx[Now][0].f=a[L][idx]; Mx[Now][0].s=c[L]; Mx[Now][1].f=-inf; Mx[Now][1].s=-1; return ; } int Mid=(L+R)>>1; build(Now<<1,L,Mid); build(Now<<1|1,Mid+1,R); pushup(Now); } void update(int Now,int L,int R,int pos) { if(L==R){ Mx[Now][0].f=a[L][idx]; Mx[Now][0].s=c[L]; Mx[Now][1].f=-inf; Mx[Now][1].s=-1; return ; } int Mid=(L+R)>>1; if(pos<=Mid) update(Now<<1,L,Mid,pos); else update(Now<<1|1,Mid+1,R,pos); pushup(Now); } void query(int Now,int L,int R,int l,int r) { if(l<=L&&r>=R){ if(Mx[Now][0].s!=res[idx][0].s){ if(Mx[Now][0].f>res[idx][0].f){ res[idx][1]=max(res[idx][0],Mx[Now][1]); res[idx][0]=Mx[Now][0]; } else { res[idx][1]=max(res[idx][1],Mx[Now][0]); } } else { rep(i,0,1) res[idx][i]=max(res[idx][i],Mx[Now][i]); } return ; } int Mid=(L+R)>>1; if(l<=Mid) query(Now<<1,L,Mid,l,r); if(r>Mid) query(Now<<1|1,Mid+1,R,l,r); } }T[4]; int main() { int Cas,N,M,Fcy=0,opt,L,R,K,C;ll X,Y; scanf("%d",&Cas); rep(i,0,3) T[i].idx=i; while(Cas--){ scanf("%d%d",&N,&M); rep(i,1,N) scanf("%I64d%I64d%I64d",&x[i],&y[i],&c[i]); rep(i,1,N) { a[i][0]=x[i]+y[i]; a[i][1]=x[i]-y[i]; a[i][2]=-x[i]+y[i]; a[i][3]=-x[i]-y[i]; } rep(i,0,3) T[i].build(1,1,N); printf("Case #%d: ",++Fcy); while(M--){ scanf("%d",&opt); if(opt==1){ scanf("%d%I64d%I64d",&K,&X,&Y); x[K]+=X; y[K]+=Y; a[K][0]=x[K]+y[K]; a[K][1]=x[K]-y[K]; a[K][2]=-x[K]+y[K]; a[K][3]=-x[K]-y[K]; rep(j,0,3) T[j].update(1,1,N,K); } else if(opt==2){ scanf("%d%d",&K,&C); c[K]=C; rep(i,0,3) T[i].update(1,1,N,K); } else { scanf("%d%d",&L,&R); rep(i,0,3) { res[i][0].f=res[i][1].f=-inf;//mp(-inf,-1); res[i][0].s=res[i][1].s=-1; } rep(i,0,3) T[i].query(1,1,N,L,R); ll ans=0; rep(i,0,3) { if(res[i][0].s!=res[3-i][0].s) ans=max(ans,res[i][0].f+res[3-i][0].f); else { ans=max(ans,res[i][0].f+res[3-i][1].f); ans=max(ans,res[i][1].f+res[3-i][0].f); } } printf("%I64d ",ans); } } } return 0; }
G .Best ACMer Solves the Hardest Problem
题意:在二维平面上四种操作: 1,加一个带权的点; 2,删去一个点; 3,给一个点周围欧几里得距离为sqrt(k)的存在的点点权都加w; 4,查询一个到点欧几里得距离为sqrtk的点权和。
思路:发现到一个点的欧几里得距离为k的点并不多。所以我们暴力即可。 注意用 ll。
#include<bits/stdc++.h> #define ll long long #define pii pair<int,int> #define mp make_pair #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxm=10000000; const int maxn=6010; ll a[maxn][maxn];int vis[maxn][maxn],Ca,tot; vector<pii>G[maxm+10]; void init() { rep(i,0,3200){ rep(j,0,3200){ if(i*i+j*j>maxm) break; G[i*i+j*j].push_back(mp(i,j)); tot++; } } } void add(int x,int y,int w) { if(vis[x][y]!=Ca) a[x][y]=0,vis[x][y]=Ca; a[x][y]+=w; } void del(int x,int y) { vis[x][y]=0; a[x][y]=0; } void FCY(int x,int y,int w) { if(x<0||x>6000||y<0||y>6000) return ; if(vis[x][y]==Ca) a[x][y]+=w; } int query(int x,int y) { if(x<0||x>6000||y<0||y>6000) return 0; if(vis[x][y]==Ca) return a[x][y]; return 0; } void ADD(int x,int y,int k,int w) { for(int i=0;i<G[k].size();i++){ int dx=G[k][i].first,dy=G[k][i].second; FCY(x+dx,y+dy,w); if(dx!=0) FCY(x-dx,y+dy,w); if(dy!=0) FCY(x+dx,y-dy,w); if(dx!=0&&dy!=0) FCY(x-dx,y-dy,w); } } ll Query(int x,int y,int k) { ll res=0; for(int i=0;i<G[k].size();i++){ int dx=G[k][i].first,dy=G[k][i].second; res+=query(x+dx,y+dy); if(dx!=0) res+=query(x-dx,y+dy); if(dy!=0) res+=query(x+dx,y-dy); if(dx!=0&&dy!=0) res+=query(x-dx,y-dy); } return res; } int main() { int T,N,M,x,y,w,k,opt;ll ans; init(); scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); Ca++; ans=0; printf("Case #%d: ",Ca); rep(i,1,N){ scanf("%d%d%d",&x,&y,&w); add(x,y,w); } rep(i,1,M){ scanf("%d%d%d",&opt,&x,&y); x=(x+ans)%6000+1; y=(y+ans)%6000+1; if(opt==1){ scanf("%d",&w); add(x,y,w); } else if(opt==2){ del(x,y); } else if(opt==3){ scanf("%d%d",&k,&w); ADD(x,y,k,w); } else { scanf("%d",&k); printf("%lld ",ans=Query(x,y,k)); } } } return 0; }
K .Let the Flames Begin
题意:约瑟夫问题,给定N,K,M,问第M个出来的人的编号是。 K或者M<1e6;
思路:只要知道公式就大概知道怎么做的题。
我们知道第N个人出来的编号公式是: p=0 ; rep(i,1,N) p=(p+k)%i; ans=p;(假设从0开始)
而第M个出来的人的编号,我们可以假设已经进行了N-M轮,但其实我们并不关心他们的位置,我可以做完之后把他们放到对应位置即可。
所以第M个人的编号是:p=0;rep(i,1,M) p=(p+k)%(i+N-M);
当M比较小时,可以直接求; 当K比较小时,我们可以利用整除合并的思想做。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(ll i=a;i<=b;i++) using namespace std; ll solve(ll N,ll M,ll K) //N个人,每K个skip一次,第M次skip的是谁 { if(K==1) return M; ll p=0; if(M<=K){ rep(i,1,M){ p=(p+K-1)%(i+N-M)+1; } } else { ll tM=M; rep(i,1,M){ if(!tM) break; if(p+K-1>=i+N-M) tM--,p=(p+K-1)%(i+N-M)+1;//,cout<<i<<":"<<p<<endl; else { //p+kx-1<i+x-1+N-M -> x<(i-1+N-M-p)/(k-1); ll t=(i+N-M-p)/(K-1),g; if((i+N-M-p)%(K-1)==0) t--; g=min(t,tM); p=p+K*g; tM-=g; i=i+g-1; } } } return p; } int main() { int T,Ca=0; ll N,M,K; scanf("%d",&T); while(T--){ scanf("%lld%lld%lld",&N,&M,&K); printf("Case #%d: %lld ",++Ca,solve(N,M,K)); } return 0; }
J .How Much Memory Your Code Is Using?
题意:开了一些变量,问你用了多少内存。
思路:模拟即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; const int mod=1e9+7; char c[1000],a[10]={'l','o','n','g',' ','l','o','n','g'}; char b[12]={'l','o','n','g',' ','d','o','u','b','l','e'}; ll sum=0; int L; bool check1() { bool F=true; if(L<9) return false; rep(i,0,8) if(c[i]!=a[i]) return false; return true; } bool check2() { bool F=true; if(L<11) return false; rep(i,0,10) if(c[i]!=b[i]) return false; return true; } void solve() { ll tmp; L=strlen(c); if(c[0]=='b'||c[0]=='c') tmp=1; if(c[0]=='i'||c[0]=='f') tmp=4; if(check1()||c[0]=='d') tmp=8; if(c[0]=='_'||check2()) tmp=16; rep(i,0,L-1){ if(c[i]=='['){ int k=0; rep(j,i+1,L-1) if(c[j]>='0'&&c[j]<='9') k=k*10+c[j]-'0'; tmp*=k; break; } } sum+=tmp; } int main() { int T,N,Ca=0; scanf("%d",&T); while(T--){ scanf("%d ",&N); sum=0; rep(i,1,N){ gets(c); solve(); } ll F=sum/1024; if(sum%1024) F++; printf("Case #%d: %lld ",++Ca,F); } return 0; }