目录
题目链接https://ac.nowcoder.com/acm/contest/11255
C - LCS
题目
LCS(s1,s2)表示s1,s2的公共子序列
s1,s2,s3的长度是n
思路
.
代码
#include <bits/stdc++.h>
using namespace std;
int n,a[3];
pair<int,string> p[3];
vector<pair<int,int>> t;
bool cmp(pair<int,int>a,pair<int,int>b)
{
return a.second<b.second;
}
int main()
{
cin>>a[0]>>a[1]>>a[2]>>n;
t.push_back({0,a[0]});
t.push_back({1,a[1]});
t.push_back({2,a[2]});
sort(t.begin(),t.end(),cmp);
if(t[2].second+t[1].second-n>t[0].second)
{
cout<<"NO"<<endl;
return 0;
}
p[0].first=t[0].second;//min
p[1].first=t[1].second;
p[2].first=t[2].second;//max
for(int i=0; i<t[0].second; i++)
{
p[0].second+='a';
p[1].second+='a';
p[2].second+='a';
}
for(int j=0; j<t[2].second-t[0].second; j++)
{
p[1].second+='b';
p[2].second+='b';
}
for(int k=0; k<t[1].second-t[0].second; k++)
{
p[0].second+='c';
p[2].second+='c';
}
while(p[0].second.size()<n)
{
p[0].second+='d';
}
while(p[1].second.size()<n)
{
p[1].second+='e';
}
while(p[2].second.size()<n)
{
p[2].second+='f';
}
//123
if(a[0]<a[1] && a[1]<a[2])
{
cout<<p[1].second<<endl;
cout<<p[0].second<<endl;
cout<<p[2].second<<endl;
return 0;
}
//132
if(a[0]<a[1] && a[1]>=a[2] && a[0]<a[2])
{
cout<<p[0].second<<endl;
cout<<p[1].second<<endl;
cout<<p[2].second<<endl;
return 0;
}
//213
if(a[0]>a[1] && a[1]<a[2] && a[0]<=a[2])
{
cout<<p[2].second<<endl;
cout<<p[0].second<<endl;
cout<<p[1].second<<endl;
return 0;
}
//231
if(a[0]<=a[1] && a[1]>a[2] && a[0]>a[2])
{
cout<<p[0].second<<endl;
cout<<p[2].second<<endl;
cout<<p[1].second<<endl;
return 0;
}
//312
if(a[0]>a[1] && a[1]<a[2] && a[0]>a[2])
{
cout<<p[2].second<<endl;
cout<<p[1].second<<endl;
cout<<p[0].second<<endl;
return 0;
}
//321
if(a[0]>=a[1] && a[1]>=a[2])
{
cout<<p[1].second<<endl;
cout<<p[2].second<<endl;
cout<<p[0].second<<endl;
return 0;
}
return 0;
}
总结
1.最后比较的时候,分类讨论,注意122和222的情况
2.感觉自己的方法好复杂(⊙﹏⊙)
E - Tree Xor
题目
一棵树,n个结点,每个结点的值为w
已知:w的取值是[l,r],且两个节点u,v的w的异或值
求:w的取值能有多少种可能
思路
- 一颗树,如果知道了每两个父子结点之间的关系,那么当根结点确定的时候,整个树上所有的取值就确定了
- 初始化:让w[1]=0,求出所有的val[i]
- 因为a ^ b = c,a ^ c = b,所以当 w[1]=a 时,w[i] = val[i] ^ a,
即:
R[i].l <= val[i]^a <= R[i].r
即:
R[i].l^a <= val[i] <= R[i].r^a - 找出每一个区间之后,求交集,交集的个数==n的时候,满足
关键点是如何构造一段区间,使这段区间^a之后,仍然是连续的
根据题目的范围,在[0,230-1]的范围上,建线段树,其中,每段的范围都是[…000,…111]的形式(…表示前面都是相同的01序列),这样,当这个区间与a异或的时候,以为前几位一直都是相同的,所以得到的还是一个连续的区间
注意左右端点的找法:
//pos是从后往前,不同的位数
int LL=( val^l )&( ((1<<30)-1)^((1<<pos)-1) );
int RR=LL+((1<<pos)-1);
LL分成两部分:…000,假设…长度为4,…是这段区间异或a,然后把后面pos=3位全部置0
RR是右端点,直接…000(LL)+…111=…111
样例
4
0 7
1 6
2 5
3 4
1 2 0
1 3 7
2 4 6
代码
#include <bits/stdc++.h>
#define maxx 1000050
using namespace std;
typedef long long ll;
int n,cnt=0,head[maxx],val[maxx];
vector<pair<int,int>> vec;
struct range
{
ll l,r;
}R[maxx];
struct node
{
int u,v,next;
ll w;
}e[maxx];
void add(int u,int v,int w)
{
e[cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
cnt++;
}
void init(int u,int pre,int w)
{
val[u]=val[pre]^w;
for(int i=head[u];i!=-1;i=e[i].next)
{
int vv=e[i].v;
int ww=e[i].w;
if(pre==vv)
continue;
init(vv,u,ww);
}
}
void query(int L,int R,int val,int l,int r,int pos)
{
if(L<=l && R>=r)
{
int LL=( val^l )&( ((1<<30)-1)^((1<<pos)-1) );
int RR=LL+((1<<pos)-1);
vec.push_back({LL,RR});
return;
}
int mid=(l+r)>>1;
if(L<=mid)
query(L,R,val,l,mid,pos-1);
if(R>mid)
query(L,R,val,mid+1,r,pos-1);
}
int solve()
{
vector<pair<int,int>> ans;
for(int i=0;i<vec.size();i++)
{
ans.push_back({vec[i].first,1});
ans.push_back({vec[i].second+1,-1});
}
int num=0;
int nn=0;
sort(ans.begin(),ans.end());
for(int j=0;j<ans.size();j++)
{
nn+=ans[j].second;
if(nn==n)
num+=ans[j+1].first-ans[j].first;
}
return num;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>R[i].l>>R[i].r;
}
memset(head,-1,sizeof head);
for(int i=0;i<n-1;i++)
{
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
init(1,0,0);//w[1]=0时的取值
vec.push_back({R[1].l,R[1].r});
for(int i=2;i<=n;i++)
{
query(R[i].l,R[i].r,val[i],0,(1<<30)-1,30);
}
cout<<solve()<<endl;
return 0;
}
总结
1.区间的划分
2.异或
3.区间异或时,左右点的找法
F - Just a joke
题目
Alice and Bob又开始玩游戏了,在一个有n个点,m条边的无向图中,可以进行两种操作:
-
Select an edge of G and delete it from G.
-
Select a connected component of G which doesn’t have any loop, then delete it from G.
问最后谁赢
思路
题如其名,根据样例可以推断粗,点也是可以删除的。。(・∀・(・∀・(・∀・*)
对于1,删除1或3(一个点或者一条边)
对于2,删除2k-1
都是奇数
所以只跟n+m的奇偶有关
I - Inverse Pair
题目
数组a是1~n之间的数,只能对每个数进行+1或+0操作,求最小的逆序对个数
思路
没有重复的数,而且是1-n之间所有的数,对于xi和xi+1,如果前者大,给后者+1即可,如果有一串的话,如5,4,3最后也是可以抵消的,然后用树状数组
代码
#include<bits/stdc++.h>
#define maxx 200010
typedef long long ll;
using namespace std;
int n;
ll a[maxx],c[maxx],pos[maxx];
int lowbit(int x)
{
return x&-x;
}
ll add(ll x)
{
ll ans=0;
for(;x;x-=lowbit(x))
{
ans+=c[x];
}
return ans;
}
void add1(ll x)
{
for(;x<=n;x+=lowbit(x))
{
c[x]++;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
pos[a[i]]=i;
}
int f=0;
for(int i=1;i<n;i++)
{
if(pos[i+1]<pos[i] && (f==0))
{
a[pos[i]]++;
f=1;
}
else
f=0;
}
// for(int i=1;i<=n;i++)
// cout<<a[i]<<' ';
ll sum=0;
for(int i=1;i<=n;i++)
{
sum+=add(n)-add(a[i]);
// cout<<add(a[i])<<endl;
add1(a[i]);
}
cout<<sum<<endl;
return 0;
}
J - Average
题目
就是求数组a在区间≥x时的最大平均值,和,数组b在区间≥y时的最大平均值
思路
前缀和+二分
- 假设平均值为T,要找到是否有 a i + . . . + a j L > = T \frac{a_{i}+...+a_{j}}{L}>=T Lai+...+aj>=T
- 即 : ( a i − T ) + . . . + ( a j − T ) > = 0 (a_{i}-T)+...+(a_{j}-T)>=0 (ai−T)+...+(aj−T)>=0
- 令 c i = a i − T c_{i}=a_{i}-T ci=ai−T,就是求 c i + . . . + c j > = 0 c_{i}+...+c_{j}>=0 ci+...+cj>=0
- 通过前缀和可以转换为: s u m [ j ] − s u m [ i − 1 ] > = 0 sum[j]-sum[i-1]>=0 sum[j]−sum[i−1]>=0
代码
#include <iostream>
#define maxx 100050
using namespace std;
double a[maxx],b[maxx],c1[maxx],c2[maxx],sum1[maxx],sum2[maxx];
int main()
{
int n,m,x,y;
cin>>n>>m>>x>>y;
for(int i=1; i<=n; i++)
{
cin>>a[i];
}
for(int i=1; i<=m; i++)
{
cin>>b[i];
}
double l=-1e10, r=1e10;
while(r-l>1e-7)
{
double mid=(l+r)/2;
for(int i=1;i<=n;i++)
{
c1[i]=a[i]-mid;
sum1[i]=sum1[i-1]+c1[i];
}
double MIN=(1<<31)-1, MAX=-1e10;
for(int i=x;i<=n;i++)
{
MIN=min(MIN, sum1[i-x]);
MAX=max(MAX, sum1[i]-MIN);
}
if(MAX>=0)
{
l=mid;
}
else
{
r=mid;
}
}
//printf("%.10f\n",r);
double l2=-1e10, r2=1e10;
while(r2-l2>1e-7)
{
double mid=(l2+r2)/2;
for(int i=1;i<=m;i++)
{
c2[i]=b[i]-mid;
sum2[i]=sum2[i-1]+c2[i];
}
double MIN=(1<<31)-1, MAX=-1e10;
for(int i=y;i<=m;i++)
{
MIN=min(MIN, sum2[i-y]); //复制粘贴的时候,没把y改过来。。
MAX=max(MAX, sum2[i]-MIN);
}
if(MAX>=0)
{
l2=mid;
}
else
{
r2=mid;
}
}
// printf("%.10f\n",r2);
printf("%.8f\n",r2+r);
return 0;
}
总结
以后要是同一个函数使用两次或多次的时候,不要复制粘贴,写个函数吧(T_T)