所以又是一次考試。
然而這次真是莫名的詭異。
拿到題目以後,點開第一題,讀了幾遍題再結合樣例,感覺自己懂了。
然後由於有人對題意意見不一,老師講了一遍題意。
然而更不知道怎麼做了好不好。。。。
然後在老師皺着眉聽完一個小時的題意質疑後,果斷告訴我們,不!做!了!
於是我們換了一套題。
浪費了一個小時後,緊鑼密鼓的做下一套。
嗯哼,在下面。
1.排列的最大長度
題目描述
現有兩個長度爲 n 的排列 A,B,需再尋找一個排列 C,使得對於 C 中任意兩個數i,j(i小於j),滿足 Ci在A 中的位置比Cj靠前,在 B 中位置也比 Cj靠前,求這個排列 C 的最大長度。
輸入描述
第一行一個數n,表示排列的長度。
第二行 n 個正整數,爲A 排列。
第三行 n 個正整數,爲B 排列。
輸出描述
一行一個數表示排列 C的最大長度。
樣例數據
輸入
5
1 2 4 3 5
5 2 3 4 1
輸出
2
數據範圍及提示
C 可以爲{2,3},也可{2,4}
對於40%的數據,n<=5000
對於100%的數據,n<=100000
注意:C 是序列不是排列。
第一次看題的時候,感覺一定是最長公共子序列。
然後開二維數組,寫了一個O(n^2)的代碼,成功過了樣例和手寫樣例。
然後死在了超時和超空間上。
嗯,部分分40。
其實可以將a[i]在b數組中的位置記爲num[i],然後求num的最長不下降子序列。
用O(nlogn)複雜度的那種哦~
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int n,ans;
int num[100004],b[100004];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int main()
{
//freopen("t1.in","r",stdin);
//freopen("t1.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
num[read()]=i;
for(int i=1;i<=n;i++)
b[i]=num[read()];
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
{
int l=1,r=ans;
while(l<=r)
{
int mid=(l+r)/2;
if(b[i]<=num[mid])
r=mid-1;
else
l=mid+1;
}
if(l>ans)
ans++;
num[l]=b[i];
}
cout<<ans<<endl;
return 0;
}
2.覆蓋節點
題目描述
現在有一棵 n 個節點的樹,要求你選出L 條路徑使得這些路徑覆蓋的節點數最大,求這個最大節點數。
輸入描述
第一行兩個整數 n,L,分別表示節點數與路徑條數。
接下來 n-1 行每行表示一條樹邊。
輸出描述
一行一個整數表示最多覆蓋的節點數。
樣例數據
輸入
17 3
1 2
3 2
2 4
5 2
5 6
5 8
7 8
9 8
5 10
10 13
13 14
10 12
12 11
15 17
15 16
15 10
輸出
13
數據範圍及提示
Subtask1(30):1<=n<=20,1<=L<=3
Subtask2(30):1<=n,L<=3000
Subtask3(40):1<=n,L<=1000000
注:最終評測開 O2。
這道題是真沒思路的,當時就只能亂搞了。
嗯,將樹分層,葉子節點爲第一層,給其他標號,號數爲距葉子節點的最遠距離。然後取最大者,再取次大者……
嗯就這樣。
最優性:畫圖可知,一條路徑每層最多經過2*L 個結點,於是 Ans 爲上界。
可行性:將葉子節點那層稱爲外層,其餘爲內層,於是注意內層點,當內層點覆蓋完全後,餘下的葉結點可任意匹配來構成路徑,於是可知路徑條數不超過L。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#define N 1000010
using namespace std;
struct node
{
int next;
int to;
}bian[N*2];
int n,l,x,y,tot,t,h,ans;
int first[N],d[N],q[N],g[N],deep[N];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline void create(int x,int y) //建邊。
{
tot++;
bian[tot].next=first[x];
first[x]=tot;
bian[tot].to=y;
}
int main()
{
freopen("t2.in","r",stdin);
freopen("t2.out","w",stdout);
n=read(),l=read();
l=l<<1;
for(int i=1;i<n;i++)
{
x=read(),y=read();
create(x,y);
create(y,x);
d[x]++;
d[y]++;
}
h=t=0;
for(int i=1;i<=n;i++)
if(d[i]==1) //記錄葉子節點。
q[++t]=i;
while(h!=t)
{
h++;
g[deep[h]]++; //多一個點加入。
int x=q[h];
for(int i=first[x];i;i=bian[i].next)
{
if(--d[bian[i].to]==1)//葉子節點之後的一層成爲了葉子節點。
q[++t]=bian[i].to,deep[t]=deep[h]+1;
}
}
for(int i=0;g[i];i++)
{
if(l<g[i])
ans+=l;
else
ans+=g[i];
}
cout<<ans<<endl;
return 0;
}
3.系列維護
題目描述
現在需要你維護一個序列a[i] ,一開始都是 0,要支持以下兩種操作:
1. U k x 把 a[k] 修改爲 x
2. Z c s 在序列上選出 c 個正數,把他們都減去 1,詢問能不能做 s 次。
詢問不會對序列產生影響。
輸入描述
第一行兩個正整數 n,m 表示序列長度和操作次數。
接下來 m 行爲m 個操作。
輸出描述
對於每個詢問操作,如果能輸出”Yes”(不含引號),不能輸出“No”。
樣例數據
輸入
3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1
輸出
No
Yes
No
Yes
數據範圍及提示
Subtask1(30): n,m,x<=1000
Subtask2(20): n,m<=1000
Subtask3(10): n=40000,m=40000,x<=10000,只有一個點
Subtask4(20):n=50000,m=200000,x<=30000 (不滿)
Subtask5(20):n,m<=1000000
注:最終評測開 O2。
設個數大於 s 的數字有k 個(如果 k大於c,顯然是 Yes。這裏討論 (k<=c),那麼如果個數小於s 的數字和不小於(c - k) * s,那麼一定有解。證明很顯然對吧_(:зゝ∠)
用兩個樹狀數組或是線段樹即可。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int getint()
{
int i=0,f=1;
char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=1e6+5;
struct node
{
int op,x,y;
}q[N];
int n,m,mx,a[N],b[N],cnt[N<<2];
long long sum[N<<2];
inline void lsh()
{
sort(b+1,b+mx+1);
mx=unique(b+1,b+mx+1)-b-1;
for(int i=1;i<=m;i++)
q[i].y=lower_bound(b+1,b+mx+1,q[i].y)-b;
}
inline void update(int k)
{
cnt[k]=cnt[k<<1]+cnt[k<<1|1];
sum[k]=(long long)sum[k<<1]+sum[k<<1|1];
}
inline void modify(int k,int l,int r,int p,int v)
{
if(l==r)
{
cnt[k]+=v;
sum[k]+=(long long)v*b[l];
return;
}
int mid=l+r>>1;
if(p<=mid)
modify(k<<1,l,mid,p,v);
else
modify(k<<1|1,mid+1,r,p,v);
update(k);
}
int querycnt(int k,int l,int r,int x,int y)
{
if(x>y||l>r)
return 0;
if(x<=l&&r<=y)
return cnt[k];
int mid=l+r>>1;
if(y<=mid)
return querycnt(k<<1,l,mid,x,y);
else if(x>mid)
return querycnt(k<<1|1,mid+1,r,x,y);
else
return querycnt(k<<1,l,mid,x,mid)+querycnt(k<<1|1,mid+1,r,mid+1,y);
}
long long querysum(int k,int l,int r,int x,int y)
{
if(x>y||l>r)
return 0;
if(x<=l&&r<=y)
return sum[k];
int mid=l+r>>1;
if(y<=mid)
return querysum(k<<1,l,mid,x,y);
else if(x>mid)
return querysum(k<<1|1,mid+1,r,x,y);
else
return (long long)querysum(k<<1,l,mid,x,mid)+querysum(k<<1|1,mid+1,r,mid+1,y);
}
int main()
{
//freopen("t3.in","r",stdin);
//freopen("t3.out","w",stdout);
char c;
n=getint(),m=getint();
for(int i=1;i<=m;i++)
{
for(c=getchar();c!='U'&&c!='Z';c=getchar());
if(c=='U')
q[i].op=0;
else
q[i].op=1;
q[i].x=getint(),q[i].y=getint();
b[++mx]=q[i].y;
}
lsh();
for(int i=1;i<=m;i++)
{
if(q[i].op==0)
{
if(a[q[i].x])
modify(1,1,mx,a[q[i].x],-1);
a[q[i].x]=q[i].y;
modify(1,1,mx,a[q[i].x],1);
continue;
}
if(q[i].op==1)
{
q[i].x-=querycnt(1,1,mx,q[i].y,mx);
long long res=querysum(1,1,mx,1,q[i].y-1);
if(res/b[q[i].y]>=q[i].x)
puts("Yes");
else puts("No");
}
}
return 0;
}
很好。
來自2017.7.27
——我認爲return 0,是一個時代的終結。