所以又是一次考试。
然而这次真是莫名的诡异。
拿到题目以后,点开第一题,读了几遍题再结合样例,感觉自己懂了。
然后由于有人对题意意见不一,老师讲了一遍题意。
然而更不知道怎么做了好不好。。。。
然后在老师皱着眉听完一个小时的题意质疑后,果断告诉我们,不!做!了!
于是我们换了一套题。
浪费了一个小时后,紧锣密鼓的做下一套。
嗯哼,在下面。
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,是一个时代的终结。