今天,我做出了模版题3道,练习题1道。
T1割点:
题目背景
割点
题目描述
给出一个nn个点,mm条边的无向图,求图的割点。
输入输出格式
输入格式:
第一行输入n,mn,m
下面mm行每行输入x,yx,y表示xx到yy有一条边
输出格式:
第一行输出割点个数
第二行按照节点编号从小到大输出节点,用空格隔开
输入输出样例
输入样例#1: 复制
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
输出样例#1: 复制
1
5
说明
对于全部数据,n \le 20000n≤20000,m \le 100000m≤100000
点的编号均大于00小于等于nn。
tarjan图不一定联通。
#include<bits/stdc++.h>
using namespace std;
struct edge{
int u,v;
}e[2000005];
bool v[200005],pd,zz[200005];
int n,m,f[200005],ne[2000005],dfn[200005],low[200005],tree[200005],now,front[200005],tot;
inline int read(){
int k=0,f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())
if(c=='-')
f=-1;
for(;isdigit(c);c=getchar())
k=k*10+c-'0';
return k*f;
}
inline void dfs(int x){
low[x]=dfn[x]=++now;
v[x]=true;
for(int i=f[x];i;i=ne[i])
if(!v[e[i].v])
tree[e[i].v]=x,dfs(e[i].v);
return ;
}
inline void bfs(int x){
v[x]=true;
for(int i=f[x];i;i=ne[i]){
int y=e[i].v;
if(tree[y]==x||tree[x]!=y){
if(!v[y])
bfs(y);
}
if(tree[y]==x)
low[x]=min(low[x],low[y]);
else if(tree[x]!=y)
low[x]=min(low[x],dfn[y]);
}
return;
}
int main(){
n=read();
m=read();
for(int i=1;i<=2*m;i+=2){
tree[i]=i,tree[i+1]=i+1;
e[i+1].v=e[i].u=read(),e[i+1].u=e[i].v=read();
ne[i]=f[e[i].u],f[e[i].u]=i;
ne[i+1]=f[e[i+1].u],f[e[i+1].u]=i+1;
}
for(int i=1;i<=n;i++){
if(!v[i])
dfs(i),front[++tot]=i,zz[i]=true;
}
memset(v,false,sizeof(v));
for(int i=1;i<=tot;i++)
bfs(front[i]);
for(int k=1;k<=tot;k++)
for(int i=f[front[k]];i;i=ne[i]){
int y=e[i].v;
if(dfn[front[k]]<=low[y]){
if(pd){
cout<<front[k]<<endl;
break;
}
else
pd=true;
}
}
for(int i=1;i<=n;i++){
if(zz[i])
continue;
for(int j=f[i];j;j=ne[j]){
int y=e[j].v;
if(low[y]>=dfn[i]){
cout<<i<<endl;
break;
}
}
}
return 0;
}
/*
9 11
6 9
8 9
6 8
6 7
1 6
1 5
2 5
4 5
3 4
2 3
1 2
*/
T2负环:
题目描述
暴力枚举/SPFA/Bellman-ford/奇怪的贪心/超神搜索
寻找一个从顶点1所能到达的负环,负环定义为:一个边权之和为负的环。
输入输出格式
输入格式:
第一行一个正整数T表示数据组数,对于每组数据:
第一行两个正整数N M,表示图有N个顶点,M条边
接下来M行,每行三个整数a b w,表示a->b有一条权值为w的边(若w<0则为单向,否则双向)
输出格式:
共T行。对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号)。
输入输出样例
输入样例#1: 复制
2
3 4
1 2 2
1 3 4
2 3 1
3 1 -3
3 3
1 2 3
2 3 4
3 1 -8
输出样例#1: 复制
N0
YE5
说明
n\leq 2000n≤2000m\leq 3000m≤3000-10000\leq w\leq 10000−10000≤w≤10000T\leq 10T≤10建议复制输出格式中的字符串。 本题数据感谢@negiizhao的精心构造,请不要使用玄学算法 本题数据有更新
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define IL inline
#define RI register int
#define N 100086
#define clear(a) memset(a,0,sizeof a)
#define rk for(RI i=1;i<=n;i++)
using namespace std;
IL void read(int &x)
{
int f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,m,T;
struct code{int u,v,w;}edge[N];
bool vis[N];
int head[N],tot,dis[N],cnt[N];
IL void add(int x,int y,int z){edge[++tot].u=head[x];edge[tot].v=y;edge[tot].w=z;head[x]=tot;}
IL bool spfa(int now)
{
rk vis[i]=false,dis[i]=2147483647,cnt[i]=false;
queue<int>q;
q.push(now);
vis[now]=true;
dis[now]=0;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=false;
if(cnt[u]>=n)return true;
for(RI i=head[u];i;i=edge[i].u)
{
if(dis[edge[i].v]>dis[u]+edge[i].w)
{
dis[edge[i].v]=dis[u]+edge[i].w;
if(!vis[edge[i].v])
{
q.push(edge[i].v);
vis[edge[i].v]=true;
cnt[edge[i].v]++;
if(cnt[edge[i].v]>=n)return true;
}
}
}
}
return false;
}
int main()
{
read(T);
while(T--)
{
read(n),read(m);
tot=0;clear(head);
for(RI i=1,u,v,w;i<=m;i++)
{
read(u),read(v),read(w);
if(w<0)add(u,v,w);
else add(u,v,w),add(v,u,w);
}
puts(spfa(1)?"YE5":"N0");
}
}
T3最长公共子序列:
题目描述
给出1-n的两个排列P1和P2,求它们的最长公共子序列。
输入输出格式
输入格式:
第一行是一个数n,
接下来两行,每行为n个数,为自然数1-n的一个排列。
输出格式:
一个数,即最长公共子序列的长度
输入输出样例
输入样例#1: 复制
5
3 2 1 4 5
1 2 3 4 5
输出样例#1: 复制
3
说明
【数据规模】
对于50%的数据,n≤1000
对于100%的数据,n≤100000
#include<iostream>
#include<cstdio>
using namespace std;
int a[100001],b[100001],map[100001],f[100001];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){scanf("%d",&a[i]);map[a[i]]=i;}
for(int i=1;i<=n;i++){scanf("%d",&b[i]);f[i]=0x7fffffff;}
int len=0;
f[0]=0;
for(int i=1;i<=n;i++)
{
int l=0,r=len,mid;
if(map[b[i]]>f[len])f[++len]=map[b[i]];
else
{
while(l<r)
{
mid=(l+r)/2;
if(f[mid]>map[b[i]])r=mid;
else l=mid+1;
}
f[l]=min(map[b[i]],f[l]);
}
}
cout<<len;
return 0;
}
T4借教室:
题目描述
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来nn天的借教室信息,其中第ii天学校有r_iri个教室可供租借。共有mm份订单,每份订单用三个正整数描述,分别为d_j,s_j,t_jdj,sj,tj,表示某租借者需要从第s_jsj天到第t_jtj天租借教室(包括第s_jsj天和第t_jtj天),每天需要租借d_jdj个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供d_jdj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第s_jsj天到第t_jtj天中有至少一天剩余的教室数量不足d_jdj个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
输入输出格式
输入格式:
第一行包含两个正整数n,mn,m,表示天数和订单的数量。
第二行包含nn个正整数,其中第ii个数为r_iri,表示第ii天可用于租借的教室数量。
接下来有mm行,每行包含三个正整数d_j,s_j,t_jdj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从11开始的整数编号。
输出格式:
如果所有订单均可满足,则输出只有一行,包含一个整数00。否则(订单无法完全满足)
输出两行,第一行输出一个负整数-1−1,第二行输出需要修改订单的申请人编号。
输入输出样例
输入样例#1: 复制
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出样例#1: 复制
-1
2
说明
【输入输出样例说明】
第 11份订单满足后,44天剩余的教室数分别为 0,3,2,30,3,2,3。第 22 份订单要求第 22天到第 44 天每天提供33个教室,而第 33 天剩余的教室数为22,因此无法满足。分配停止,通知第22 个申请人修改订单。
【数据范围】
对于10%的数据,有1≤ n,m≤ 101≤n,m≤10;
对于30%的数据,有1≤ n,m≤10001≤n,m≤1000;
对于 70%的数据,有1 ≤ n,m ≤ 10^51≤n,m≤105;
对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n1≤n,m≤106,0≤ri,dj≤109,1≤sj≤tj≤n。
NOIP 2012 提高组 第二天 第二题
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Tree{
int l,r;
ll ma,add;
}t[8000040];
ll n,m,a[1000020],cho,x,y,k;
inline ll read(){
ll k=0,f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())
if(c=='-')
f=-1;
for(;isdigit(c);c=getchar())
k=k*10+c-'0';
return k*f;
}
inline void build(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(l==r){
t[p].ma=a[l];
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].ma=min(t[p*2].ma,t[p*2+1].ma);
return ;
}
inline void spread(int p){
if(t[p].add){
t[p*2].ma+=t[p].add;
t[p*2].add+=t[p].add;
t[p*2+1].ma+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p].add=0;
}
return;
}
inline void change(int p){
if(x<=t[p].l&&y>=t[p].r){
t[p].ma+=k;
t[p].add+=k;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
if(x<=mid)
change(p*2);
if(mid<y)
change(p*2+1);
t[p].ma=min(t[p*2].ma,t[p*2+1].ma);
return;
}
inline ll ask(int p){
ll num=1e11;
spread(p);
if(t[p].l>=x&&t[p].r<=y)
return t[p].ma;
int mid=(t[p].l+t[p].r)/2;
if(mid>=x)
num=min(ask(p*2),num);
if(mid<y)
num=min(ask(p*2+1),num);
return num;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
k=-read(),x=read(),y=read();
change(1);
ll ans=ask(1);
if(ans<0){
printf("-1\n%d",i);
return 0;
}
}
cout<<0;
return 0;
}