#3258 【HNOI2004】L语言
#2204 秘密消息
#1359 传送带
#4147 【2018NOIP提高测试1026】naive 的图 (graph)
#1903 第k大的数
【以上题目均出自WOJ(其他网站也有)】
#3258 【HNOI2004】L语言
trie树
线性DP
用dp[i]记录前i位是否可以,方程很简单,看一下就懂了。
trie树基础就不说了吧,用来存储题目里的英文字典。
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0;
char s[1000010],s1[12];
int t[100000][30],val[100000],tot=0;
int dp[1000010];
void insert(){
int l=strlen(s1),p=0;
for(int i=0;i<l;i++){
int c=s1[i]-'a';
if(!t[p][c])t[p][c]=++tot;
p=t[p][c];
}
val[p]=1;
}
int Find(int x,int y){
int p=0;
for(int i=x;i<=y;i++){
int c=s[i]-'a';
if(!t[p][c]){
p=0;break;
}
p=t[p][c];
}
return val[p];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s1);
int u=strlen(s1);
len=max(len,u);
insert();
}
while(m--){
scanf("%s",s);
memset(dp,0,sizeof(dp));
int l=strlen(s),ans=0;
for (int j=0;j<l;j++)
for (int k=max(j-len,-1);k<=j;k++)
if ((k==-1||dp[k])&&Find(k+1,j)){
dp[j]=1;ans=j+1;break;
}
printf("%d\n",ans);
}
return 0;
}
#2204 秘密消息
trie树
将每次的路径中的每一个点的w++,代表有w个单词经过这里。
一个单词插完以后,val++,trie树基本,不过可能重复,所以不是bool类型。
查询有两种情况:
- 该信息全部走完
- 再往下走没有与该信息相符的节点
第一种情况,路径上的val累加(不算当前节点),再加上当前点w(避免重复)
第二种情况,路径上的val累加(算当前节点)
#include<bits/stdc++.h>
using namespace std;
int n,m,c,t[10000010][2],tot=0,val[10000010],w[10000010];
vector<int>q;
void insert(){
int len=q.size(),p=0;
for(int i=0;i<len;i++){
if(!t[p][q[i]])t[p][q[i]]=++tot;
p=t[p][q[i]];
w[p]++;//
}
val[p]++;
}
int ask(){
int len=q.size(),p=0,ans=0;
for(int i=0;i<len;i++){
if(!t[p][q[i]])return ans;
ans+=val[t[p][q[i]]];
p=t[p][q[i]];
}
ans+=w[p];
ans-=val[p];
return ans;
}
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
scanf("%d",&c);
for(int j=1;j<=c;j++){
int u;
scanf("%d",&u);
q.push_back(u);
}
insert();
q.clear();
}
while(n--){
scanf("%d",&c);
for(int j=1;j<=c;j++){
int u;
scanf("%d",&u);
q.push_back(u);
}
printf("%d\n",ask());
q.clear();
}
return 0;
}
#1359 传送带
三分套三分
题解:%%%XLY%%%
#include<bits/stdc++.h>
using namespace std;
#define eps 1e-8
double l1,l2,xa,xb,ya,yb,xc,xd,yc,yd,v1,v2,v3;
double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double calc(double x,double y){//放大并求值
double x1=xa+(xb-xa)*x;
double y1=ya+(yb-ya)*x;
double x2=xd-(xd-xc)*y;
double y2=yd-(yd-yc)*y;
return l1*x/v1+l2*y/v2+dis(x1,y1,x2,y2)/v3;
}
double sanfen_2(double a){
double l=0,r=1,midl,midr;
while(r-l>=eps){
midl=l+(r-l)/3.0;
midr=r-(r-l)/3.0;
if(calc(a,midl)<calc(a,midr))r=midr;
else l=midl;
}
return calc(a,l);
}
double sanfen_1(){
double l=0,r=1,midl,midr;
while(r-l>=eps){
midl=l+(r-l)/3.0;
midr=r-(r-l)/3.0;
if(sanfen_2(midl)<sanfen_2(midr))r=midr;
else l=midl;
}
return sanfen_2(l);
}
int main(){
scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",&xa,&ya,&xb,&yb,&xc,&yc,&xd,&yd,&v1,&v2,&v3);
l1=dis(xa,ya,xb,yb);l2=dis(xc,yc,xd,yd);
printf("%.2lf",sanfen_1());
return 0;
}
#4147 naive 的图 (graph)
kruskal
生成树
权值线段树
线段树合并
持久化线段树
题解:%%%LDX%%%
#include<bits/stdc++.h>
using namespace std;
struct edge{
int u,v,w;
}e[500010];
inline bool Sort_5(const edge &x,const edge &y){
return x.w<y.w;
}
int n,m,k,sig,tot=0;
long long ans=0;
int fa[200010],c[200010][2],val[400010],root[200010][2];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct Tree{
int l,r,sum;
}t[10000010];
void insert(int &x,int l,int r,int v){
if(!x)x=++tot;//
//t[x]=t[y];//还没合并
t[x].sum++;
if(l==r)return;
int mid=l+r>>1;
if(v<=mid)insert(t[x].l,l,mid,v);
else insert(t[x].r,mid+1,r,v);
}
int merge(int x,int y,int l,int r){//线段树合并
if(!x||!y)return x+y;
t[x].sum+=t[y].sum;
if(l==r)return x;
int mid=l+r>>1;
t[x].l=merge(t[x].l,t[y].l,l,mid);
t[x].r=merge(t[x].r,t[y].r,mid+1,r);
return x;
}
void query(int x,int y,int l,int r,int w){
if(!x||!y||l==r)return;
int mid=l+r>>1;
ans+=1ll*w*t[t[x].l].sum*t[t[y].r].sum;
query(t[x].l,t[y].l,l,mid,w);
query(t[x].r,t[y].r,mid+1,r,w);
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
fa[i]=i;
scanf("%d",&c[i][0]);
val[i*2-1]=c[i][0];
val[i*2]=c[i][1]=c[i][0]+k-1;
}
sort(val+1,val+n+n+1);
int sig=unique(val+1,val+n+n+1)-val-1;
for(int i=1;i<=n;i++){
c[i][0]=lower_bound(val+1,val+sig+1,c[i][0])-val;
c[i][1]=lower_bound(val+1,val+sig+1,c[i][1])-val;
insert(root[i][0],1,sig,c[i][0]);
insert(root[i][1],1,sig,c[i][1]);
}
//读入+离散
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e+1,e+m+1,Sort_5);
for(int i=1;i<=m;i++){//Kruskal
int x=find(e[i].u),y=find(e[i].v);
if(x==y)continue;
fa[x]=y;
if(k){
query(root[x][1],root[y][0],1,sig,e[i].w);
query(root[y][1],root[x][0],1,sig,e[i].w);
}
else ans+=1ll*e[i].w*t[root[x][0]].sum*t[root[y][0]].sum;
root[y][0]=merge(root[y][0],root[x][0],1,sig);
root[y][1]=merge(root[y][1],root[x][1],1,sig);//线段树合并
}
printf("%lld",ans);
return 0;
}
#1903 第k大的数
描述
你为Macrohard公司的数据结构部门工作,
你的工作是重新写一个数据结构,
这个数据结构能快速地找到一段数列中第k大的数。
就是说,给定一个整数数列a[1…n],其中每个元素都不相同,
你的程序要能回答一组格式为Q (i , j , k)的查询,
Q(i, j ,k)的意思是“在a[i…j]中第k大的数是多少?”
例如令 a = {1, 5, 2, 6, 3, 7, 4},查询格式为Q (2 , 5 , 3),
数列段a[2…5] = {5, 2, 6, 3},第3大的数是5,所以答案是5。
输入
文件第一行包括一个正整数n,代表数列的总长度,
还有一个数m,代表有m个查询。
n,m满足:1≤n≤100 000, 1≤m≤5 000 第二行有n个数,
代表数列的元素,所有数都不相同,而且不会超过109 接下来有m行,
每行三个整数i , j , k,代表一次查询, i , j , k满足1≤i≤j≤n, 1≤k≤j − i + 1
输出
输出每个查询的答案,用换行符隔开
样例输入
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
样例输出
5
6
3
标签
shoi2006
权值线段树
持久化线段树
神奇的是第K小?
#include<bits/stdc++.h>
using namespace std;
int n,m,q,a[200010],b[200010],h=0,root[200010];
struct Tree{
int l,r,sum;
}t[3600010];
void insert(int y,int &x,int l,int r,int p){
x=++h;
t[x]=t[y];
t[x].sum++;
if(l==r)return;
int mid=l+r>>1;
if(p<=mid)insert(t[y].l,t[x].l,l,mid,p);
else insert(t[y].r,t[x].r,mid+1,r,p);
}
int query(int x,int y,int l,int r,int k){
if(l==r)return l;
int w=t[t[y].l].sum-t[t[x].l].sum;
int mid=l+r>>1;
if(w>=k)query(t[x].l,t[y].l,l,mid,k);
else query(t[x].r,t[y].r,mid+1,r,k-w);
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+m+1,a[i])-b;
for(int i=1;i<=n;i++){
insert(root[i-1],root[i],1,m,a[i]);
}
while(q--){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
//查的是a[i]的值
printf("%d\n",b[query(root[l-1],root[r],1,m,k)]);
}
return 0;
}