@01010101
2018-10-17T21:42:23.000000Z
字数 5246
阅读 858
T1
这是个二维平面世界,平面上有n个特殊的果实,我从(0,0)点出发,希望得到尽量多的果实,但是出于某种特殊的原因,我的运动方式只有三种(假设当前我在(x,y)):
1、我可以走到(x+1,y)
2、我可以走到(x,y+1)
3、我可以走到(x+1,y+1)
现在我需要你的帮助,帮我找出我最多能够得到多少个果实。
1<=n<=100000,-10^9<=x,y<=10^9
用带一点点扫描线的思想来想,如果按横坐标排序,那么数量就是纵坐标不断递增的。就是最长上升子序列了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int read(){
char ch;int op=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') op=-1;
int ret=ch-'0';
while((ch=getchar())>='0'&&ch<='9') ret=ret*10+ch-'0';
return ret*op;
}
const int N=100000+10;
int n,cnt;
int t[N];
struct node{
int xx,yy;
bool operator < (const node &b) const{
if(xx!=b.xx) return xx<b.xx;
return yy<b.yy;
}
}a[N];
int main(){
freopen("find.in","r",stdin);
freopen("find.out","w",stdout);
n=read();
for(int i=1;i<=n;++i){
a[i].xx=read();a[i].yy=read();
}
sort(a+1,a+n+1);
for(int i=1;i<=n;++i){
if(a[i].xx<0||a[i].yy<0) continue;
int b=a[i].yy;
if(b>=t[cnt]){
t[++cnt]=b;
}
else{
int l=1,r=cnt,mid,ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(t[mid]>b) {ans=mid;r=mid-1;}
else l=mid+1;
}
if(ans!=-1) t[ans]=b;
}
}
printf("%d\n",cnt);
return 0;
}
T2
这是个奇异的世界,世界上的n-1条路联结起来形成一棵树,每条路有一个对应的权值ci。
现在我会给出q组询问或操作。
每次询问我会从一个x点走到y点,初始在x点我会有一个数字v,然后每走过一条权值为c的边,我的v就会变成,问最后到y时v变成了什么。
每次修改我会修改一条边的权值,保证修改后的权值小于等于原来的权值且不会小于1。
每组询问或操作的格式如下:
询问:1 x y v表示从x走到y,一开始的数字为v。
操作:2 p c表示将第p条边的边权修改为c
n,q<=100000,c_i,v_i <= 10^{18}
显然,走过一条非1边后边权至少减半。所以有两种维护方式:1边,或0边。
对于1边:可以用并查集进行路径压缩。
对于0边:可以用树链剖分维护从当前点到根的路径乘积,用double进行乘,超过1e18就置为0。
此题不需要维护精度:
有两个结论:
1.
向下取整只与数值有关,与数的先后无关。
2.
向下取整可以变连除为除一个。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
ll read(){
char ch;ll op=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') op=-1;
ll ret=ch-'0';
while((ch=getchar())>='0'&&ch<='9') ret=ret*10+ch-'0';
return ret*op;
}
void write(ll x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
const ll N=100000+10;
const ll D=20;
ll n,m,k,q;
ll head[N],cst[N],f[N],d[N],fa[N][D+2];
struct edge{
ll v,w,nxt;
}e[N<<1];
void adde(ll u,ll v,ll w){
e[k].v=v;
e[k].w=w;
e[k].nxt=head[u];
head[u]=k++;
}
ll Find(ll x){
return (f[x]==x)?x:f[x]=Find(f[x]);
}
void dfs(ll u,ll fath){
d[u]=d[fath]+1;fa[u][0]=fath;
for(ll i=head[u];i!=-1;i=e[i].nxt){
ll v=e[i].v;
if(v!=fath){
if(e[i].w==1) f[v]=Find(u);
else f[v]=v;
dfs(v,u);
cst[v]=e[i].w;
}
}
}
ll Lca(ll u,ll v){
if(d[u]<d[v]) swap(u,v);
for(ll i=D;i>=0;--i){
if(d[fa[u][i]]>=d[v]) u=fa[u][i];
}
if(u==v) return u;
for(ll i=D;i>=0;--i){
if(fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
}
return fa[u][0];
}
ll up_tree(ll u,ll t,ll cc){
while(d[u]>d[t]&&cc!=0){
cc/=cst[u];
if(f[u]!=u) u=f[u];
else u=fa[u][0];
}
return cc;
}
ll a,b,c,op;
int main(){
// freopen("walk.in","r",stdin);
// freopen("walk.out","w",stdout);
memset(head,-1,sizeof(head));
scanf("%lld%lld",&n,&q);
for(ll i=1;i<n;++i){
a=read();b=read();c=read();
adde(a,b,c);adde(b,a,c);
}
f[1]=1;
dfs(1,0);
for(ll i=1;i<=D;++i){
for(ll j=1;j<=n;++j){
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
while(q--){
op=read();
if(op==1){
a=read();b=read();c=read();
ll l=Lca(a,b);
write(up_tree(b,l,up_tree(a,l,c)));
putchar('\n');
}
else{
a=read();b=read();
if(b==1) {
f[e[2*a-2].v]=Find(e[2*a-1].v);
}
cst[e[2*a-2].v]=b;
}
}
return 0;
}
T3
这是个有n个点的世界,有m条无向边连接着这n个点,但是不保证点之间能够互相到达。
“这个世界的夕阳,只在奇数长的简单路径的尽头。”一个神如是说。
于是我想知道对于一个点对(x,y),x到y之间的所有简单路径中是否存在长度为奇数的路径,只有这样,我才能找到存在有夕阳的路。保证没有自环与重边。
1≤n,q,m≤100000
此题的考察知识较多。
题解:
假设我们现在对原图先得到一棵生成树(其实应该是森林,毕竟不保证联通)
那么我们对于询问(x,y),我们要看的其实是在生成树上x到y的路径上是否存在在某个奇环内的边(“奇环”指长度为奇数的环)。
于是问题转化成看一条边是否在奇环中,一个很直观的想法是,对于一个强连通分量,如果在这个强连通分量中存在一条在奇环中的边,那么所有在强连通分量里的边都是存在于某一个奇环中的。
那么如何在一个强连通分量里找到是否有在奇环中的边呢?我们对这个图做tarjan,对于一条虚边(u,v)如果dep[u]与dep[v]的奇偶性相同(dep[u]表示u在树中的深度),那么u到v的路径上的边都是在奇环中的。
于是我们可以得到每条边是否存在于奇环中,问题就变得很简单了。
对于一个询问(x,y)如果dep[x]和dep[y]奇偶性不同,那么是yes的
否则如果在x到y的路径上存在有在奇环中的边,那么也是yes的,这个可以通过预处理出点到根节点的路径上的这种边的个数得到。
再否则就只有no了。
注意:弹栈要在退栈的时候弹。
const int N=100000+10;
const int D=20;
int n,m,k,tp,idx;
int head[N],d[N],fa[N][D+2],dfn[N],low[N],sta[N],odd[N],s[N],vis[N];
struct edge{
int v,nxt;
}e[N<<1];
void adde(int u,int v){
e[k].v=v;
e[k].nxt=head[u];
head[u]=k++;
}
void tarjan(int u,int fath){
d[u]=d[fath]+1;fa[u][0]=fath;
dfn[u]=low[u]=++idx;sta[++tp]=u;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(v!=fath){
if((d[u]&1)==(d[v]&1)) odd[u]=1;
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
int pos=tp,w=0;
while(sta[pos]!=u) w|=(odd[sta[pos--]]);
if(w==1) {
while(sta[tp]!=u) s[sta[tp--]]++;
}
else tp=pos-1;//!!不能再忘else了!!!!
}
}
int Lca(int u,int v){
if(d[u]<d[v]) swap(u,v);
for(int i=D;i>=0;--i){
if(d[fa[u][i]]>=d[v]) u=fa[u][i];
}
if(u==v) return u;
for(int i=D;i>=0;--i){
if(fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
}
if(fa[u][0]==fa[v][0]) return fa[u][0];
return -1;
}
void dfs(int u,int fath){
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(fa[v][0]==u){
vis[v]=1;
s[v]+=s[u];
dfs(v,u);
}
}
}
int a,b,q;
int main(){
// freopen("a.txt","r",stdin);
memset(head,-1,sizeof(head));
n=read();m=read();
for(int i=1;i<=m;++i){
a=read();b=read();
adde(a,b);adde(b,a);
}
for(int i=1;i<=n;++i){
if(!dfn[i]){
tarjan(i,i);
}
}
for(int i=1;i<=D;++i){
for(int j=1;j<=n;++j){
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
for(int i=1;i<=n;++i){
if(fa[i][0]==i){
dfs(i,i);
}
}
q=read();
while(q--){
a=read();b=read();
int l=Lca(a,b);
if(l==-1) {puts("No");continue;}
if(((d[a]+d[b]-2*d[l])&1)==1) {puts("Yes");continue;}
if(s[a]+s[b]>2*s[l]) {puts("Yes");continue;}
puts("No");
}
}