1002 Nonsense Time
赛中和队友一直在搞一个胡搞做法,艰难优化到本地跑10s,可惜始终过不了。
最后只好补了题解做法。。。
倒着删除,用树状数组求LIS,维护一条当前的LIS的路径。若被删除的数不在当前的LIS中,则答案不变;否则,重新做一遍LIS。
据说因为LIS的期望长度为sqrt(n),所以被选中的概率是,所以总时间复杂度为
。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int maxn=5e4+100;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int seed=131;
int t,n,ans,c[maxn],pos[maxn],pre[maxn],maxp;
int p[maxn],k[maxn],num,pp;
bool vis[maxn];
inline int lowbit(int x){
return x&(-x);
}
inline void modify(int x,int z){
int tmp=x;
while(tmp<=n){
if(z>c[tmp]) c[tmp]=z,pos[tmp]=x;
tmp+=lowbit(tmp);
}
}
inline void query(int x){
while(x){
if(c[x]>num) num=c[x],pp=pos[x];
x-=lowbit(x);
}
}
inline void solve(){
ans=0,maxp=0;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
memset(pos,0,sizeof(pos));
for(int i=1;i<=n;i++){
if(p[i]){
num=0,pp=0;
query(p[i]-1);
pre[p[i]]=pp;
modify(p[i],num+1);
if(num+1>ans){
ans=num+1,maxp=p[i];
}
}
}
while(maxp){
vis[maxp]=1;
maxp=pre[maxp];
}
}
int out[maxn];
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
for(int i=1;i<=n;i++) scanf("%d",&k[i]);
solve();
for(int i=n;i>=1;i--){
out[i]=ans;
if(vis[p[k[i]]]){
p[k[i]]=0;
solve();
}
else p[k[i]]=0;
}
for(int i=1;i<=n;i++){
cout<<out[i];
if(i!=n) cout<<" ";
else cout<<"\n";
}
}
return 0;
}
1005 Snowy Smile
首先要知道线段树是怎么求最大子段和的:维护当前区间的最大子段和,当前区间从左往右的最大子段和,当前区间从右往左的最大子段和,然后就可以合并了。
对于本题,只有2000个点,时间复杂度显然是可以的。
想到先对所有点按x排序,将y离散化,并用离散化后在数组中的下标代替原来的值。
按x枚举,先枚举左边的边(即较小的x),当左边的边确定时,右边的边从左往右一列列的扫,同时将w加在对应的y上,用线段树来维护,每次修改都是logn的,每枚举完一列查询一次最大子段和的值,用一个变量维护最大值即可。
枚举左边的边是O(n)的,枚举右边的边、修改线段树中的值和查询的总过程可认为是O(nlogn)的,因此总的时间复杂度为。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int maxn=2e3+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int seed=131;
int t,m,cnt;
ll ans;
struct node{
int l,r;
ll sum,maxx,lmax,rmax;
}Q[maxn<<2];
struct Point{
int x,y,w;
bool operator<(const Point &p)const{
return x<p.x||x==p.x&&y<p.y;
}
}a[maxn];
int yy[maxn];
void build(int rt,int l,int r){
Q[rt].l=l,Q[rt].r=r,Q[rt].sum=Q[rt].lmax=Q[rt].maxx=Q[rt].rmax=0;
if(l==r) return ;
int mid=(Q[rt].l+Q[rt].r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
}
inline void pushup(int rt){
Q[rt].sum=Q[ls].sum+Q[rs].sum;
Q[rt].maxx=max(max(Q[ls].maxx,Q[rs].maxx),Q[ls].rmax+Q[rs].lmax);
Q[rt].lmax=max(Q[ls].lmax,Q[ls].sum+Q[rs].lmax);
Q[rt].rmax=max(Q[rs].rmax,Q[rs].sum+Q[ls].rmax);
}
void show(int rt){
cout<<Q[rt].l<<" "<<Q[rt].r<<" "<<Q[rt].sum<<" "<<Q[rt].maxx<<"\n";
}
void update(int rt,int p,int x){
// show(rt);
if(Q[rt].l==Q[rt].r){
Q[rt].sum+=x;
Q[rt].lmax=Q[rt].maxx=Q[rt].rmax=Q[rt].sum;
return;
}
int mid=(Q[rt].l+Q[rt].r)/2;
if(p<=mid) update(ls,p,x);
else update(rs,p,x);
pushup(rt);
}
int rt=1,l=1,r,n;
int main(){
// freopen("in.txt","r",stdin);
a[0].x=-1e9-1;
scanf("%d",&t);
while(t--){
ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w),yy[i]=a[i].y;
sort(yy+1,yy+n+1);
cnt=unique(yy+1,yy+n+1)-yy;
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
a[i].y=lower_bound(yy+1,yy+cnt,a[i].y)-yy;
}
a[n+1].x=1e9+1;
for(int i=1;i<=n;i++){
if(a[i].x==a[i-1].x) continue;
rt=1,l=1,r=cnt-1;
build(rt,l,r);
for(int j=i;j<=n;j++){
while(a[j+1].x==a[j].x){
// cout<<i<<" "<<j<<" "<<"\n";
update(rt,a[j].y,a[j].w);
j++;
}
update(rt,a[j].y,a[j].w);
ans=max(ans,Q[1].maxx);
}
}
cout<<ans<<"\n";
}
return 0;
}
1006 Faraway
赛中枚举区间部分没有想的很清楚,之所以没有仔细想下去是因为不会做枚举完区间后CRT。。。
不过题解做法不用CRT,就按题解的思路补了这题。
首先考虑如何枚举区间。可以把每个方程的x和y的值看作坐标系上两条分别平行y轴和x轴的线段,另一维的取值为[0,m]。
这样就有至多n条平行于x轴的线段和n条平行于y轴的线段,形成O(n^2)个矩形区域。
区域确定后,所有方程都可以去绝对值。
考虑到lcm(2,3,4,5)=60,即对60取模后相同的值,对2、3、4、5分别取模后值也相同。
因此可以分别枚举和
的模数,通过模数对所有方程进行check,如果全部符合要求,则求出该区域中
对应模数个数和
对应模数个数的乘积,并加入答案。因为
和
每种模数的个数是无关的,可以预处理求出对应方向每个区域每种模数的个数。
预处理O(n*60)+区域个数O()*枚举两个模数O(
)*checkO(n)
所以总的时间复杂度O()
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int maxn=10+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=60;
const int seed=131;
int t,n,m;
struct node{
int x,y,k,t;
}a[maxn];
int xx[maxn],yy[maxn],cntx,cnty;
int numx[maxn][65],numy[maxn][65];
inline void init(){//计算区间模数的个数
memset(numx,0,sizeof(numx));
memset(numy,0,sizeof(numy));
for(int i=0;i<cntx;i++){
if(xx[i]>xx[i+1]-1) continue;
for(int j=0;j<mod;j++) numx[i][j]=(xx[i+1]-1)/mod+(j<=(xx[i+1]-1)%mod)-(xx[i]/mod+(j<(xx[i]%mod)));
}//(xx[i+1]-1)/60
for(int i=0;i<cnty;i++){
if(yy[i]>yy[i+1]-1) continue;
for(int j=0;j<mod;j++) numy[i][j]=(yy[i+1]-1)/mod+(j<=(yy[i+1]-1)%mod)-(yy[i]/mod+(j<(yy[i]%mod)));
}
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d %d %d %d",&a[i].x,&a[i].y,&a[i].k,&a[i].t),xx[i]=a[i].x,yy[i]=a[i].y;
sort(xx+1,xx+1+n);
sort(yy+1,yy+1+n);
cntx=unique(xx+1,xx+1+n)-xx;
cnty=unique(yy+1,yy+1+n)-yy;
xx[cntx]=m+1;
yy[cnty]=m+1;
xx[0]=0;
yy[0]=0;
init();
ll ans=0;
for(int i=0;i<cntx;i++){//[xx[i],xx[i+1]-1]
if(xx[i]>xx[i+1]-1) continue;
for(int j=0;j<cnty;j++){//[yy[j],yy[j+1]-1]
if(yy[j]>yy[j+1]-1) continue;
for(int m1=0;m1<mod;m1++){
if(numx[i][m1]==0) continue;
for(int m2=0;m2<mod;m2++){
if(numy[j][m2]==0) continue;
bool f=0;
for(int z=1;z<=n&&!f;z++){
int tmp1=m1-a[z].x,tmp2=m2-a[z].y;
if(xx[i]<a[z].x) tmp1=-tmp1;
if(yy[j]<a[z].y) tmp2=-tmp2;
tmp1=((tmp1+tmp2)%a[z].k+a[z].k)%a[z].k;
//虽然值大于x或y,但是模数可能小于x或y,因此可能出现负值
if(tmp1!=a[z].t) f=1;
}
if(!f) ans+=numx[i][m1]*1ll*numy[j][m2];
}
}
}
}
cout<<ans<<"\n";
}
return 0;
}
1008 TDL
显然与n互质的第m个数和n的差值不会太大,随便估计一下2000肯定够用了,那么差值与n的异或相较n的差距也不会超过2000。
所以枚举,作为n,这样可以得到当前的f(n,m),再从n+1开始枚举到f(n,m)或第m个与n互质的数。当此时的f(n,m)就是第m个和n互质的数即为答案。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int maxn=1e5+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int seed=131;
int t,m,cnt;
ll n,k,ff,tt;
bool f=0;
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--){
f=0;
scanf("%lld %d",&k,&m);
for(n=max(1ll,k-2000);n<=k+2000&&!f;n++){
ff=(k^n)+n;
// cout<<n<<" "<<ff<<"\n";
if(__gcd(ff,n)!=1||ff<m+n) continue;
cnt=0;
for(tt=n+1;tt<=ff;tt++){
if(__gcd(tt,n)==1){
cnt++;
if(cnt==m) break;
}
}
if(cnt<m) continue;
else if(tt==ff){
f=1;break;
}
}
if(!f) n=-1;
cout<<n<<"\n";
}
return 0;
}
1012 Stay Real
签到题。
因为小根堆中最大的值一定是叶子,所以两个人轮流取当前最大的值即可,大根堆维护方便好写。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int maxn=1e5+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int seed=131;
int t,n,x,tt;
ll ans[2];
priority_queue<int> pq;
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
pq.push(x);
}
tt=ans[0]=ans[1]=0;
while(!pq.empty()){
ans[tt]+=pq.top();
pq.pop();
tt^=1;
}
cout<<ans[0]<<" "<<ans[1]<<"\n";
}
return 0;
}