20181029小结

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值