一定要保護自己的夢想,即使犧牲一切。
前言
把人給考沒了。。。
看出來 T1 是一個週期性的東西了,先是打了一個暴力,想著打完 T2 T3 暴力就回來打。。
然後,就看著 T2 上頭了,後來發現是看錯題了,碼完暴力就已經 2.5h 了
接下來就會開始看 T3 看到了部分分非常令人欣喜(碼起主席樹根本停不下來)。
一直到考試結束都沒想起我那 T1 。
T1 最長不下降子序列
解題思路
對於比較小的資料可以直接 \(mathcal{O(nlogn)}\) 求出來(洗提 30 pts)
發現對於同一遞推式,同一膜數,一定會有一個不大於膜數的週期(抽屜原理)
當出現一個與之前相同的數字時,後面的數其實也是一樣的。
然後整個序列就成了三部分:最前面的散的+可以被劃分成幾個週期的區間+不完整的週期
接下來先對於第一部分以及它後面的一個週期可以直接暴力求出來。
並且以此類推直接將後面的區間中週期數加上就好了。
對於第三部分的轉移不可以只向前倒一個週期,這樣是不對的。
所有可以向前倒 週期個週期 這樣就可以完美地解決這個問題了。
邊界問題需要卡一下。
最後一個問題,如果運算的答案比正確答案少 1 怎麼辦??加上不就好了,有問題麼,沒有問題。(反正我的邊界是這麼卡出來的)
code(除錯資訊未刪)
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e6+10,INF=1e18;
int n,a,b,c,d,T,t0,beg,len,ans,f[N],s[N],pos[N];
int cnt,lsh[N],tre[N];
int lowbit(int x)
{
return x&(-x);
}
int ask(int x)
{
int temp=0;
for(int i=x;i<=cnt+1;i+=lowbit(i))
temp=max(temp,tre[i]);//,cout<<i<<endl;
return temp;
}
void add(int x,int num)
{
for(int i=x;i;i-=lowbit(i))
tre[i]=max(tre[i],num);//,cout<<i<<endl;
}
void Special_Judge()
{
lsh[++cnt]=-INF;
lsh[++cnt]=s[1]=t0;
for(int i=2;i<=n;i++)
lsh[++cnt]=s[i]=(a*s[i-1]%d*s[i-1]%d+b*s[i-1]%d+c)%d;
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(int i=1;i<=n;i++)
s[i]=lower_bound(lsh+1,lsh+cnt+1,s[i])-lsh;
// for(int i=1;i<=cnt;i++) cout<<lsh[i]<<' ';
for(int i=1;i<=n;i++)
{
f[i]=ask(cnt+1-s[i])+1;
// cout<<f[i]<<' ';
add(cnt+1-s[i],f[i]);
ans=max(ans,f[i]);
}
printf("%lld",ans);
// cout<<endl;for(int i=1;i<=n;i++) cout<<s[i]<<' ';
exit(0);
}
signed main()
{
n=read();
t0=read();
a=read();
b=read();
c=read();
d=read();
s[1]=t0;
for(int i=2;i<=min(n,2*d);i++)
{
s[i]=(a*s[i-1]%d*s[i-1]%d+b*s[i-1]%d+c)%d;//,cout<<s[i]<<' ';
// cout<<s[i]<<endl;
}
// f();
for(int i=1;i<=min(n,2*d);i++)
{
// cout<<i<<' '<<s[i]<<endl;
if(pos[s[i]])
{
// cout<<s[i]<<' '<<pos[s[i]]<<endl;
T=i-pos[s[i]];
beg=pos[s[i]];
break;
}
else pos[s[i]]=i;
}
if(!beg||T*T+beg-1>=n||n<=1e6) Special_Judge();
// f();
// cout<<T<<' '<<beg<<endl;
cnt=1000;
for(int i=1;i<=beg+T-1;i++)
{
// f();
// cout<<s[i]<<' '<<cnt<<endl;
f[i]=ask(cnt-s[i]+1)+1;
// if(f[i]) f();
add(cnt-s[i]+1,f[i]);
}
// f();
// cout<<(n-beg+1)/T<<endl;
for(int i=1;i<=T;i++)
{
// f();
f[i]=f[i+beg-1]+(n-beg+1)/T-T;
ans=max(f[i],ans);
// cout<<f[i]<<endl;
}
// for(int i=1;i<=10;i++) cout<<s[i]<<' ';
// for(int i=beg;i<=beg+T-1;i++) cout<<s[i]<<' ';cout<<endl;
len=(n-beg+1)%T;
s[0]=s[beg+T-1];
for(int i=1;i<=T*T+len-1;i++)
s[i]=(a*s[i-1]%d*s[i-1]%d+b*s[i-1]%d+c)%d;
// for(int i=1;i<=T+len-1;i++) cout<<s[i]<<' ';
memset(tre,0,sizeof(tre));
/*
for(int i=1;i<=T;i++)
{
// cout<<f[i]<<' ';
f[i]=max(f[i],ask(cnt-s[i]+1)+1);
// cout<<ask(cnt-s[i]+1)+1<<endl;
// cout<<f[i]<<endl;
add(cnt-s[i]+1,f[i]);
}
// */
// cout<<T<<' '<<beg<<' '<<len<<endl;
for(int i=1;i<=T;i++)
{
memset(tre,0,sizeof(tre));
for(int j=i;j<=T;j++)
add(cnt-s[j]+1,f[j]);
for(int j=T+1;j<=((len>0)?T*T+len-1:T*T);j++)
{
// cout<<s[j]<<' ';
int temp=ask(cnt-s[j]+1)+1;
// cout<<temp<<endl;
ans=max(ans,temp);
add(cnt-s[j]+1,temp);
}
}
printf("%lld",ans);
return 0;
}
T2 完全揹包問題
解題思路
有一個新知識:同餘最短路。
通俗來講就是其它的數對於整個序列中最小的數字(這樣時間複雜度較小)取膜後。
所得到的餘數,對於更大的數只要與序列最小數取膜後餘數也是這個,那麼就一定可以通過序列中某些數字的組合得到。
然後發現這個題的 DP 可以對於前幾個物品,選擇了幾個有限制的物品,以及對於序列最小數取膜之後的值進行維護。
有限制的物品比較好考慮,主要是沒有限制的。
如果列舉的話,可以列舉到正無窮,顯然這樣是不可以的。
因此考慮上面提到的同餘最短路,若干個相同數字的加和取膜最終一定會得到之前取膜得到過的數。
對於這個性質,我們可以直接跑最短路,但是也可以對於沒一個“環”進行維護。
每加入一個數,就更新相同數的不同餘數的最小值,可以遞迴實現。
然後應用到此題上就是對於每一個加入的沒有限制的數,直接通過兩個迴圈分別找到這個“環”並且進行更新就好了。
對於所有的都有限制的資料進行特殊處理就好了(題庫好像並沒有這樣的資料)
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e4+10,M=3e5+10,INF=4557430888798830399ll;
int n,m,l,c,s[60],f[60][N];
bool vis[60][N];
bitset<M> bj[60];
void solve1()
{
bj[0][0]=true;
for(int i=1;i<=n;i++)
for(int j=1;j<=c;j++)
bj[j]|=bj[j-1]<<s[i];
for(int i=1,x;i<=m;i++)
{
bool jud=false;
x=read();
for(int j=0;j<=c;j++)
if(bj[j][x])
{
jud=true;
break;
}
if(jud) printf("Yes\n");
else printf("No\n");
}
}
void solve2()
{
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=2;i<=n;i++)
if(s[i]<l)
{
memset(vis,false,sizeof(vis));
for(int j=0;j<=c;j++)
for(int k=0;k<s[1];k++)
if(!vis[j][k])
{
int temp=k,minn=INF,id,now;
while(!vis[j][temp])
{
vis[j][temp]=true;
if(minn>f[j][temp])
{
minn=f[j][temp];
id=temp;
}
temp=(temp+s[i])%s[1];
}
now=(id+s[i])%s[1];
while(now!=id)
{
f[j][now]=min(f[j][now],minn+s[i]);
minn=f[j][now];
now=(now+s[i])%s[1];
}
}
}
else
for(int j=1;j<=c;j++)
for(int k=0;k<s[1];k++)
f[j][k]=min(f[j][k],f[j-1][((k-s[i])%s[1]+s[1])%s[1]]+s[i]);
for(int i=1,x;i<=m;i++)
{
x=read();
bool jud=false;
for(int j=0;j<=c;j++)
if(f[j][x%s[1]]<=x&&f[j][x%s[1]]!=INF)
{
jud=true;
break;
}
if(jud) printf("Yes\n");
else printf("No\n");
}
}
signed main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
s[i]=read();
l=read();
c=read();
sort(s+1,s+n+1);
if(s[1]>=l) solve1();
else solve2();
return 0;
}
T3 最近公共祖先
解題思路
可以說是本次考試中最水的題了。
不難發現對於每一個新加入的黑點,造成影響的其實只有以下幾部分:
-
以該黑色節點為根節點的子樹。
-
該黑色節點的所有祖先以及各級祖先除了該節點所在子樹的子樹。
然後直接在 DFS 序上維護線段樹,區間修改單點查詢,然後在已經更改過的節點打上標記就好了。
考場是看上了那個部分分,然後碼了棵主席樹,然後我就炸了。。
code
正解
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=2e5+10;
int n,m,s[N],top,sta[N];
int tim,id[N],fa[N],siz[N],dfn[N],dep[N];
int all,root[N];
int tot=1,head[N],nxt[N],ver[N];
int vis[N],flag;
string ch;
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
struct Segment_Tree
{
int dat,laz;
}tre[N<<2];
void dfs(int x)
{
siz[x]=1;
dfn[x]=++tim;
id[tim]=x;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
fa[to]=x;
dep[to]=dep[x]+1;
dfs(to);
siz[x]+=siz[to];
}
}
void push_down(int x)
{
if(!tre[x].laz) return ;
tre[ls].laz=max(tre[ls].laz,tre[x].laz);
tre[rs].laz=max(tre[rs].laz,tre[x].laz);
tre[ls].dat=max(tre[ls].dat,tre[x].laz);
tre[rs].dat=max(tre[rs].dat,tre[x].laz);
tre[x].laz=0;
}
void push_up(int x)
{
tre[x].dat=max(tre[ls].dat,tre[rs].dat);
}
void update(int x,int l,int r,int L,int R,int num)
{
if(L>R) return ;
if(L<=l&&r<=R)
{
tre[x].dat=max(tre[x].dat,num);
tre[x].laz=max(tre[x].laz,num);
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,num);
if(R>mid) update(rs,mid+1,r,L,R,num);
push_up(x);
}
int query(int x,int l,int r,int pos)
{
if(l==r) return tre[x].dat;
push_down(x);
int mid=(l+r)>>1;
if(pos<=mid) return query(ls,l,mid,pos);
return query(rs,mid+1,r,pos);
}
void change(int x)
{
update(1,1,tim,dfn[x],dfn[x]+siz[x]-1,s[x]);
while(x&&!vis[x])
{
vis[x]++;
update(1,1,tim,dfn[fa[x]],dfn[x]-1,s[fa[x]]);
update(1,1,tim,dfn[x]+siz[x],dfn[fa[x]]+siz[fa[x]]-1,s[fa[x]]);
x=fa[x];
}
}
signed main()
{
vis[1]=true;
n=read();
m=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(1);
for(int i=1,opt,x;i<=m;i++)
{
cin>>ch>>x;
if(ch[0]=='M')
{
flag=true;
change(x);
}
else if(!flag) printf("%d\n",-1);
else printf("%lld\n",query(1,1,tim,dfn[x]));
}
return 0;
}
35pts 主席樹
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=2e5+10;
int n,m,s[N],top,sta[N];
int tim,fa[N],siz[N],dfn[N],dep[N],topp[N],son[N];
int all,root[N];
int cnt,lsh[N];
int tot=1,head[N],nxt[N],ver[N];
bool flag=false;
int vis[N];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
struct Node
{
int opt,x;
}q[N];
struct Segment_Tree
{
int l,r,siz;
}tre[N*80];
int insert(int pre,int l,int r,int pos)
{
int x=++all;
tre[x]=tre[pre];
tre[x].siz++;
if(l==r) return x;
int mid=(l+r)>>1;
if(pos<=mid) ls=insert(tre[pre].l,l,mid,pos);
else rs=insert(tre[pre].r,mid+1,r,pos);
return x;
}
void dfs(int x)
{
if(vis[x]>=2)
{
for(int i=head[x];i;i=nxt[i])
if(fa[ver[i]]==x)
{
root[ver[i]]=insert(root[x],1,cnt,s[x]);
dfs(ver[i]);
}
}
else if(vis[x]==1)
{
// f();
// cout<<"Check: "<<x<<endl;
for(int i=head[x];i;i=nxt[i])
if(fa[ver[i]]==x)
{
if(!vis[ver[i]]) root[ver[i]]=insert(root[x],1,cnt,s[x]);
else root[ver[i]]=root[x];
dfs(ver[i]);
}
}
else
{
for(int i=head[x];i;i=nxt[i])
if(fa[ver[i]]==x)
{
root[ver[i]]=root[x];
dfs(ver[i]);
}
}
}
void dfs3(int x)
{
// f();
// cout<<x<<endl;
if(vis[x]) vis[x]=2;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
dfs3(to);
vis[x]+=(vis[to]!=0);
}
// cout<<"Check: "<<x<<' '<<vis[x]<<endl;
}
int query(int pre,int x,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1,rsum=tre[rs].siz-tre[tre[pre].r].siz;
if(rsum) return query(tre[pre].r,rs,mid+1,r);
return query(tre[pre].l,ls,l,mid);
}
bool b[N];
void Special_Judge()
{
add(0,1);
int tmp=0;
for(int i=1;i<=m;i++)
if(!q[i].opt)
vis[q[i].x]=b[q[i].x]=1,tmp++;
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(int i=1;i<=n;i++)
s[i]=lower_bound(lsh+1,lsh+cnt+1,s[i])-lsh;
dfs3(1);
dfs(0);
for(int i=1,ans;i<=m;i++)
{
if(!q[i].opt) continue;
if(b[q[i].x]) ans=lsh[s[q[i].x]];
else ans=0;
for(int j=head[q[i].x];j;j=nxt[j])
if(vis[ver[j]])
{
ans=max(ans,lsh[s[q[i].x]]);
break;
}
if(!tmp) printf("%lld\n",-1ll);
else printf("%lld\n",max(ans,lsh[query(root[0],root[q[i].x],1,cnt)]));
}
exit(0);
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
fa[to]=x;
dep[to]=dep[x]+1;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
signed main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
lsh[++cnt]=s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=m;i++)
{
string ch;
cin>>ch;
q[i].x=read();
if(ch[0]=='Q') q[i].opt=1;
else q[i].opt=0;
if(!q[i].opt&&q[i-1].opt)
flag=true;
}
dfs1(1);
dfs2(1,1);
if(!flag) Special_Judge();
for(int i=1;i<=m;i++)
if(!q[i].opt) sta[++top]=q[i].x;
else if(!top) printf("%lld\n",-1ll);
else
{
int maxn=0;
for(int j=1;j<=top;j++)
maxn=max(maxn,s[LCA(sta[j],q[i].x)]);
printf("%lld\n",maxn);
}
return 0;
}