K:number:
签到水题,略。
A:meeting
题意:
在一个树上有m个人,每一秒每个人可以移动一条边。求这m个人移动到同一点所花费的最小时间。
思路:
只需要找m个人中,路径最远的两个人的距离的一半儿就可以了。两边bfs或者dfs求d[]数组即可。时间复杂度:O(n+m)
AC代码:
#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
int d[MAXN];
int vis[MAXN];
int a[MAXN];
int head[MAXN],e[MAXN],nexts[MAXN];
int o;
void add(int x,int y)
{
e[++o]=y;
nexts[o]=head[x];head[x]=o;
}
void bfs(int x)
{
// memset(d,0,sizeof(d));
queue<int>q;
q.push(x);
d[x]=0;
while(!q.empty())
{
x=q.front();
q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=head[x];i;i=nexts[i])
{
int y=e[i];
if(vis[y])continue;
d[y]=d[x]+1;
q.push(y);
}
}
return ;
}
int main(void)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&a[i]);
}
bfs(a[1]);
int x=a[1];
// printf("%d\n",x);
for(int i=1;i<=m;i++)
{
if(d[a[i]]>d[x])x=a[i];
}
// for(int i=1;i<=n;i++)printf("d[%d]=%d\n",i,d[i]);
for(int i=1;i<=n;i++)vis[i]=0;;
bfs(x);
int ans=0;
for(int i=1;i<=m;i++)
{
ans=max(ans,d[a[i]]);
}
// for(int i=1;i<=n;i++)printf("d[%d]=%d\n",i,d[i]);
printf("%d",(ans+1)/2);
return 0;
}
J:free
题意:
给一张图,可以把k条边的权值变为0,求从起点到终点的最短路径。
思路:
在djstla找最短路的算法中,d数组开两维,第一维是点,第二维是把几条边的权值变成了0,直接跑一边最短路即可。由于加了剪枝,时间复杂度:O(n*k)。
AC代码:
#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e4+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
struct state
{
int x,k,w;
bool operator <(const state &a)const
{
return w>a.w;
}
};
ll d[MAXN][MAXN];
int head[MAXN],nexts[MAXN],e[MAXN],w[MAXN];
int o;
void add(int x,int y,int z)
{
e[++o]=y;w[o]=z;nexts[o]=head[x];head[x]=o;
}
int n,m,s,t,k;
ll djstla()
{
for(int i=0;i<=n;i++)
for(int j=0;j<=k;j++)
d[i][j]=INF;
for(int i=0;i<=k;i++)d[s][i]=0;
priority_queue<state>q;
state now,next;
now.x=s;now.k=0;now.w=0;
q.push(now);
while(!q.empty())
{
now=q.top();
q.pop();
// if(vis[now.x])continue;
// vis[now.x]=1;
if(now.w!=d[now.x][now.k])continue;//目前队列中存在更优的状态
if(now.x==t)return d[now.x][now.k];
for(int i=head[now.x];i;i=nexts[i])
{
int y=e[i];
if(d[y][now.k]>d[now.x][now.k]+w[i]){
d[y][now.k]=d[now.x][now.k]+w[i];
next.x=y;next.w=d[y][now.k];next.k=now.k;
q.push(next);
}
if(now.k<k&&d[y][now.k+1]>d[now.x][now.k]){
d[y][now.k+1]=d[now.x][now.k];
next.x=y;next.w=d[now.x][now.k];next.k=now.k+1;
q.push(next);
}
}
}
}
int main(void)
{
scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
for(int i=0;i<m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
printf("%lld",djstla());
return 0;
}
D: triples I
题意:
给定一个数a,求一个各个元素都是3的倍数的数组,使各元素进行或操作之后的结果是a。输出最小长度的符合要求的数组。
思路:
考虑到a的二进制状态,以及在二进制中,奇数(下标从1开始)为上的1膜3之后的余数是1,偶数位上的1膜3之后的余数是2。所以直接根据给的a进行构造即可。可以发现最多只需要两个数即可。需要分类讨论,因为a的情况很多。比如如果a%3=1,那么需要构造这两个数一个数需要去掉a的奇数位上的一个1,另外一个数需要去掉四个,或者去掉两个偶数位上的1。需要对a的奇数位和偶数位上的1的数量进行讨论。
代码:
//略
C:sequence
题意: 给a【】,b【】两个数组,求一个区间 l~r 使min(a[l] ~ a[r]) * sum(b[l] ~ b[r]) 的值最大。输出最大值。
思路: (不知道和题解是不是一样)
枚举每个以a[i]为最小值的最大区间 l~r,然后对于b数组,如果a[i]>0,先找到 b数组中i ~ r的区间前缀和的最大值ma,再找 l ~ i 的区间前缀和的最小值mi,然后用 a[i]*(ma-mi)更新答案。如果 a[i]<0 ,那么对b数组的操作相反即可。
对于a数组可以用单调栈的方法求l和r。对于b数组,维护前缀和的区间最大值的线段树。
AC代码:
#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e7+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
struct NODE
{
int num;
int l;
int r;
}a[MAXN];
struct segment_tree
{
int l,r;
ll w[2];//sum数组区间最大值与最小值。0表示最大值,1表示最小值
}t[MAXN];
ll b[MAXN];
int n;
void update_a()//单调栈求以a[i]为最小值的最大区间
{
int i,j;
stack<int> monostack;
for(i=1;i<=n;i++)
{
while(!monostack.empty() && a[monostack.top()].num > a[i].num)
{
a[monostack.top()].r=i-1;
a[i].l=a[monostack.top()].l;
monostack.pop();
}
monostack.push(i);
}
if(!monostack.empty())
j=monostack.top();
while(!monostack.empty())
{
a[monostack.top()].r=j;
monostack.pop();
}
}
void build(int l,int r,int p)//建立b数组前缀和的线段树
{
t[p].l=l;
t[p].r=r;
if(l==r){t[p].w[0]=t[p].w[1]=b[l];return;}
int mid=(l+r)/2;
build(l,mid,p*2);
build(mid+1,r,p*2+1);
t[p].w[0]=max(t[p*2].w[0],t[p*2+1].w[0]);
t[p].w[1]=min(t[p*2].w[1],t[p*2+1].w[1]);
return;
}
ll ask(int l,int r,int p,int f)//查询最大值或者最小值
{
// printf("%d %d %d\n",l,r,p);
ll ans;
if(f==0)ans=-ll_INF;
else ans=ll_INF;
if(l<=t[p].l&&r>=t[p].r)return t[p].w[f];
int mid=(t[p].l+t[p].r)/2;
if(f==0)
{
if(l<=mid)ans=max(ans,ask(l,r,p*2,f));
if(r>mid)ans=max(ans,ask(l,r,p*2+1,f));
}
else
{
if(l<=mid)ans=min(ans,ask(l,r,p*2,f));
if(r>mid)ans=min(ans,ask(l,r,p*2+1,f));
}
return ans;
}
int main(void)
{
ll ans=-INF;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].num);
a[i].l=a[i].r=i;
}
for(int i=1;i<=n;i++)
{
int mid;
scanf("%d",&mid);
ans=max(ans,(ll)a[i].num*(ll)mid);
b[i]=b[i-1]+(ll)mid;//b数组就是题目b数组的前缀和
}
update_a();
build(1,n,1);
// ok(1);
// for(int i=1;i<=n;i++)
// {
// cout<<a[i].num<<":"<<a[i].l<<" "<<a[i].r<<endl;
// }
for(int i=1;i<=n;i++)
{
if(a[i].num==0)ans=max(ans,0ll);
else if(a[i].num<0)
{
// ok(1);
ll mi=ask(i,a[i].r,1,1);
ll ma=ask(a[i].l,i,1,0);
// ok(2);
ans=max(ans,(ll)a[i].num*(mi-ma));
}
else
{
// ok(3);
ll ma=ask(i,a[i].r,1,0);
// ok(4);
ll mi=ask(a[i].l,i,1,1);
ans=max(ans,(ll)a[i].num*(ma-mi));
}
}
printf("%lld",ans);
return 0;
}
附:牛客网的oj很迷,数据量开够不是报T就是段错误。非要把数据量开到题意的10倍才会AC。