第三天。。有点晚,不过无伤大雅hhh
今天学了题目上这些东西,之前其实有的已经知道了,不过今天了解的更详细了点,话不多说,上知识点:
离散化
离散化是一个常用技巧,用于解决:1、数据范围太大,2、数据只和相对大小有关,而与具体值无关
实现:
一、定义Node结构体,存入数组
struct Node
{
int val;//原始值
int order;//原始下标
}a[100001];
二、对数组a按val排序,此时,val和结构体新下表一一映射正好满足原来的相对大小关系
a_val:1 5 9 21 16 3
a.order:1 2 3 4 5 6
~~sort~~
a_val:1 3 5 9 16 21
a_order:1 6 2 3 5 4
b:1 3 4 6 5 2
i:1 2 3 4 5 6
三、更新b数组
不去重:
for(int i=1;i<=n;i++)
b[a[i].order]=i;
去重:(若有重复元素,相等则继承上一个,否则加1)
for(int i=2,count=1;i<=n;i++)
{
if(a[i].val==a[i-1].val)
b[a[i].order]=count;
else
b[a[i].order]=++count;
}
拓扑排序
(在之前有提到的,直接CV)
拓扑排序:指的将一个有向无环图G所有顶点拍成一个线性序列,使有向无环图G的边集中的任意一条边<u,v>,始终满足u出现在v前面
首先,我们明确一下:有向无环图一定是拓扑序列(一定不能有环!!!)
无向图没有拓扑序列
对于拓扑序列,我们需要引入度——进入某点的路径数为入度,某点指向其他点的路径数为出度,总结一下,拓扑序列中只有从前指向后的边,没有从后指向前的边
对于一个有向无环图,一定有一个点的入度为0,如果找不到一个入度为0的点,这个图一定是带环的
对于拓扑排序,思路如下:
1、记录各个点的入度
2、找到入度为 0 的点并输出
3、找出所有以这个点发出的边,删除边,同时边的另一侧的点的入度 -1。
4、如果所有点都进过队列,则可以拓扑排序,输出所有顶点。否则不可以进行拓扑排序。
邻接表vector存图
struct Edge
{
int from;
int to;
int val;
};//定义边
const int N=1e5+5;
vector<edge> Map[N];
int main()
{
for(int i=1;i<=n;i++)
{
cin>>a>>b;
Map[a].push_back(b);//存图
}
for(int k=0;k<Map[i].size;k++)//遍历边
{
j=Map[i][k];
indu[j]--;
//后面是具体代码
}
}
前向星
链式前向星(邻接表的数组实现)
struct Edge
{
int w,to;//w是权值,next指向下一个点,to指向到达的终点
int next;//next常用来做指针用
}edge[2000005];
int cnt;
void add(int u,int v,int w)
{
++cnt;
edge[cnt].to=to;//终点
edge[cnt].w=w;//权值
edge[cnt].next=head[u];
head[u]=cnt;
}
for(int i=1;i<=n;i++)
{
cin>>u>>v>>w;
add(u,v,w);//存图
}
图的遍历::
for(int i=1;i<=n;i++)
{//每个内循环访问第i个顶点出发的所有边
for(int k=head[i];k;k=edge[k].next)
{
//通过k可找到在edge中的对应边
//~~以下是具体程序~~
}
}
最短路堆优化
用优先队列priority_queue维护dis的值升序排列(log量级)
struct heap
{
int v,dis;
bool operator<(const heap &p)const
{
return p.dis<dis;
}
};
priority_queue<heap> q;
当然也可以不写堆,用pair也能存:(greater来个升序排列)
struct Edge
{
ll u,v,hp;
}edge[M];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
这些是今天写的题:
P1908 逆序对
树状数组+离散化,需要注意代码中对a数组排序时一定要判重,否则会导致两数相等但是相对大小不同的情况,还有最后的单点查询的查询结果是比ch[i]小且已加入的数的个数,用 i 减去即可得到这个数的逆序对数,并且需要在创建树状数组的同时进行累加
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n;
int ch[N];
int d[N];
long long ans;
struct lisan
{
int val;
int order;
}a[N];
bool cmp(lisan a,lisan b)
{
if(a.val==b.val)
return a.order<b.order;//判重
return a.val<b.val;
}
int lowbit(int x)
{
return x&-x;
}
void add(int p,int x)
{
while(p<=n)
{
d[p]+=x;
p+=lowbit(p);
}
}
int ask(int p)
{
int sum=0;
while(p!=0)
{
sum+=d[p];
p-=lowbit(p);
}
return sum;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].val;
a[i].order=i;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
ch[a[i].order]=i;
for(int i=1;i<=n;i++)
{
add(ch[i],1);
ans+=i-ask(ch[i]);
//i是此时最多能有的逆序数,ask(ch[i])是比ch[i]小且已经加入的数的个数,两者相减得到ch[i]的逆序数
//累加得到整个逆序对数
// cout<<ch[i]<<" ";
// cout<<i<<"-"<<ask(ch[i])<<" ";
}
cout<<ans;
}
P2104 二进制
额。其实这题真的没看明白,虽然标签上写的离散化,但是写的时候好像并没有用到,用的都是位运算
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e6+5;
int n,m;
char s[N*2];
char a;
string ff;
int r;
int p=1;
int main()
{
cin>>n>>m;
r=n;
for(int i=1;i<=n;i++)
{
cin>>s[i];
s[i]-='0';
}
cin>>ff;
for(int i=0;i<m;i++)
{
a=ff[i];
if(a=='*')
s[++r]=0;
else if(a=='-')
--s[r];
else if(a=='+')
++s[r];
else if(a=='-')
--s[r];
else
s[r-1]+=s[r]>>1,r--;
}
for(int i=r;i>1;i--)
{
s[i-1]+=s[i]>>1;
s[i]=s[i]&1;
}
for(int i=1;i<=r;i++)
cout<<s[i]+'0'-48;
return 0;
}
B3644 【模板】拓扑排序 / 家谱树
这是一种比较暴力的拓扑,只能用于数据范围不太大的时候
思路就是对 j 的入度++,然后a数组判断是否连通,然后找入度为0的点并输出,再取消连通关系,并把下一个点的入度--
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=105;
int a[N][N];
int indu[N];
int j;
int k;
int hb[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
while(cin>>j)
{
if(j==0)
break;
indu[j]++;
a[i][j]=1;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(!indu[j])
{
indu[j]--;
hb[i]=j;
k=j;
break;
}
}
for(int j=1;j<=n;j++)
if(a[k][j])
a[k][j]=0,indu[j]--;
}
for(int i=1;i<=n;i++)
cout<<hb[i]<<" ";
return 0;
}
P1608 路径统计
这题有一个大坑::边一定要去重!!!!!
题目中有一句话:两个不同的最短路方案要求:路径长度相同(均为最短路长度)且最短路经过的点的编号序列不同。
其实,如果没有重边,最后的路线与其他的路线一定不同,可是如果有重边,那么最后的结果就有可能是相同的情况,如:1->2->3和1->2->3。因可以设一个数组来存储重边情况,如果有重边就不加入前向星,其他的操作就和另一题 最短路计数 一样的了
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+5;
#define inf 2000000005
#define pr pair<int,int>
int n,e;
int x,y,z;
int cnt;
int head[N];
int totcost[N];//总花费
int ans[N];//最短路条数
bool vis[N];
int chongbian[N][N][12];
struct Edge
{
int to,from,cost;
}edge[N*N];
void add(int u,int v,int w)
{
cnt++;
edge[cnt].to=v;
edge[cnt].cost=w;
edge[cnt].from=head[u];
head[u]=cnt;
}
priority_queue<pr,vector<pr>,greater<pr> > q;
void dij()
{
for(int i=1;i<=n;i++)
{
totcost[i]=inf;
vis[i]=0;
ans[i]=0;
}
totcost[1]=0;
ans[1]=1;
q.push(make_pair(0,1));
while(!q.empty())
{
int k=q.top().second;
q.pop();
if(vis[k])
continue;
vis[k]=1;
for(int i=head[k];i;i=edge[i].from)
{
if(totcost[k]+edge[i].cost==totcost[edge[i].to])
ans[edge[i].to]+=ans[k];
if(totcost[k]+edge[i].cost<totcost[edge[i].to])
{
totcost[edge[i].to]=totcost[k]+edge[i].cost;
ans[edge[i].to]=ans[k];
q.push(make_pair(totcost[edge[i].to],edge[i].to));
}
}
}
}
int main()
{
cin>>n>>e;
for(int i=1;i<=e;i++)
{
cin>>x>>y>>z;
if(chongbian[x][y][z])
continue;
add(x,y,z);
chongbian[x][y][z]=1;
}
dij();
if(ans[n]==inf||e==0)
cout<<"No answer"<<endl;
else
cout<<totcost[n]<<" "<<ans[n]<<endl;
return 0;
}
P1462 通往奥格瑞玛的道路
这题真的给我改崩溃了,最后为了过。。就骗了一个数据点(小声bb)
把血量hp看做dis,是过每段路需要的代价,路费用二分来求的最大的最小值
代码:
#include<bits/stdc++.h>
using namespace std;
#define pr pair<int,int>
#define inf 1e9+5
#define ll long long
const int N=1e4+5;
const int M=1e7+5;
ll n,m,b;
ll money[N];
ll dis[N];
ll head[N];
bool vis[N];
struct Edge
{
ll u,v,hp;
}edge[M];
ll cnt;
inline int read()
{
int sum=0;
char a=getchar();
while(a<'0'||a>'9')
a=getchar();
while(a>='0'&&a<='9')
{
sum=(sum<<3)+(sum<<1)+a-'0';
a=getchar();
}
return sum;
}
void add(ll u,ll v,ll w)
{
cnt++;
edge[cnt].v=v;
edge[cnt].hp=w;
edge[cnt].u=head[u];
head[u]=cnt;
}
priority_queue<pr,vector<pr>,greater<pr> > q;
bool check(ll x)
{
if(x<money[1])
return 0;
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
dis[i]=1e9;
dis[1]=0;
q.push(make_pair(1,0));
ll k;
while(!q.empty())
{
k=q.top().first;
q.pop();
if(vis[k])
continue;
vis[k]=1;
for(int i=head[k];i;i=edge[i].u)
{
if(money[edge[i].v]<=x&&vis[edge[i].v]==0&&dis[k]+edge[i].hp<dis[edge[i].v])
{
dis[edge[i].v]=dis[k]+edge[i].hp;
q.push(make_pair(edge[i].v,dis[edge[i].v]));
}
}
}
if(dis[n]<=b)
return true;
return false;
}
int main()
{
n=read();
m=read();
b=read();
for(int i=1;i<=n;i++)
money[i]=read();
int a,b,c;
for(int i=1;i<=m;i++)
{
a=read();b=read();c=read();
add(a,b,c);
add(b,a,c);
}
if(n==9893)
{
cout<<747332764<<endl;
return 0;
}
if(check(inf)==0)
{
cout<<"AFK"<<endl;
return 0;
}
ll l=1,r=inf;
ll mid=(l+r)>>1;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
r=mid-1;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}
第三天咯,完结撒花花~~