12
27
2015
0

区间第k大

总算会写主席树了好开桑~

题目描述

老师给大家n个不同的数字a1,a2, ... , an, 问你从小到大第k个的数字这个问题很简单。但现在老师要加大难度,要大家一口气回答m个询问,每个询问给定一个区间[x, y] 问你[x, y]之间从小到大排序后第k个数是多少?

输入

输入n, m

然后一行输入n个不同的整数

然后输入m

每行输入x, y, k,表示询问[x, y]区间里从小到大排序后第k个数是多少

 

输出

对于每个询问输出第k个数字

样例输入

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

样例输出

5
6
3

对每个前缀建一棵线段树,然后寻找第k个数就可以辣

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define N 101000
#define ls(i) T[(i)].ls
#define rs(i) T[(i)].rs
#define w(i) T[(i)].w
struct  node{
    int ls,rs,w;
    node(){ls=0;rs=0;w=0;}
}T[N*20];
int id,n,m,k,t,ans,x,y,z;
int f[N],a[N],b[N],root[N],p[N];
int cmp(int i,int j){
    return a[i]<a[j];
}
void add(int &i,int l,int r,int x){
    T[++id]=T[i];i=id;
    w(i)++;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) add(ls(i),l,mid,x);
    else add(rs(i),mid+1,r,x);
}
int find(int x,int y,int l,int r,int k){
    if (l==r) return(l);
    int mid=(l+r)/2;
    int s=w(ls(y))-w(ls(x));
    if (s>=k) return(find(ls(x),ls(y),l,mid,k));
    else return(find(rs(x),rs(y),mid+1,r,k-s));
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) {scanf("%d",&a[i]);p[i]=i;}
    sort(p+1,p+n+1,cmp);
    for (int i=1;i<=n;i++) b[p[i]]=i;
    for (int i=1;i<=n;i++){
        root[i]=root[i-1];
        add(root[i],1,n,b[i]);
    }
    for (int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        ans=find(root[x-1],root[y],1,n,z);
        printf("%d\n",a[p[ans]]);
    }
}
Category: 未分类 | Tags:
12
27
2015
0

Contest20151224滚粗记

JSOI2015终于考完辣

然而这次题目极其蛋疼(还不如考JSOI。。)

所以还是决定不订正辣(谁让这套题巧妙地避开了我的知识范围)

要题目戳这

要题解戳这

T1

通项公式还是挺水的

然而不会求Σ怎么破。。

先打个20分再说

后面那p<=1000的20分好像可做

似乎有循环诶

好的40分到手

T2

听说神(S)犇(B)桢错误贪心拿了18分,略不爽

T3

看到300行代码直接弃疗

Category: 滚粗记 | Tags:
12
23
2015
3

Contest20151221滚粗记

又膜了一场JSOI

靠着暴力的80分华丽丽的拿了第一

T1

本以为是一道水题,暴力枚举原串然后贪心判断正确性就好了

然后。。被aabaabaaaa这个数据完爆。。血崩……

判断正确性还是要用区间dp

f[i,j]表示i-j是否为完美匹配

完美匹配定义为:除去完全匹配的子串,剩余子串连接起来为匹配串的前缀

这样若f[i,j-1]为完美匹配,就知道f[i,j]为完美匹配时a[j]应是什么

两区间合并只要满足两者皆为完美匹配并且其中一个长度为匹配串长度的倍数

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
char s[210],st[210],ans[210];
int n,m,k,l,t,r,cas;
int ts[200],tt[200];
bool f[210][210];
bool shit(int m){
    int pp=0;
    for (int w=0;w<l;w++){
        if (w>=m-1)
            if ((s[w]==s[l-1])&&(s[w-m+1]==s[0])){
                memset(tt,0,sizeof(tt));
                int fuck=0;
                for (int i=1;i<=m;i++){st[i]=s[w-m+i];tt[st[i]]++;}
                for (int i=1;i<=190;i++) if ((tt[i]*(l/m))!=ts[i]){fuck=1;break;}
                if (fuck) continue;
                memset(f,0,sizeof(f));
                for (int i=1;i<=l;i++) 
                    if (s[i-1]==st[1]) f[i][i]=1;
                for (int q=2;q<=l;q++)
                for (int i=1;i<=l-q+1;i++){
                    int j=i+q-1;
                    if (f[i][j-1]&&(s[j-1]==st[(q-1)%m+1])){f[i][j]=1;continue;}
                    for (int o=1;o<=(q-1)/m;o++){
                        k=i+o*m-1;
                        if (f[i][k]&&f[k+1][j]){f[i][j]=1;break;}
                        k=j-o*m;
                        if (f[i][k]&&f[k+1][j]){f[i][j]=1;break;}
                    }
                }
                if (f[1][l]){
                    if (pp==0) for (int i=1;i<=m;i++) ans[i]=st[i];
                    else for (int i=1;i<=m;i++) if (st[i]<ans[i]){for (int j=1;j<=m;j++) ans[j]=st[j];break;}
                    else if (st[i]>ans[i]) break;
                    pp=1;
                }
            }
    }
    k=m;
    if (pp) return(1);
    return(0);
}
int main(){
    scanf("%d",&cas);
    while (cas-->0){
        scanf("%s",s);
        l=strlen(s);
        memset(ts,0,sizeof(ts));
        for (int i=0;i<l;i++) ts[s[i]]++;
        for (int ii=1;ii<=l;ii++)
            if ((l%ii)==0) 
                if (shit(ii)) break;
        for (int i=1;i<=k;i++) printf("%c",ans[i]);
        printf("\n");
    }
}

T2

不难发现有一个性质,每次询问的中位数只会比之前大1或不变

因此只要判断ans是否为修改后的中位数即可

判断可用分块优化

把n个数分成块,对每块进行排序,使每块中元素单调递增

更改时,整块的可以记一个数组+1,非整块的在块里暴力找然后+1,之后再快排(暴力好可怕)

最后对每块二分求出比ans大的个数就好辣

 

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 1000010
#define a first
#define b second
struct node{
    int a,b;
};
bool cmp(node x,node y){
    return x.a<y.a;
}
pair<int,int> a[N];
int c[N],p[N],id[N],d[N];
int n,m,k,l,t,s,ans,r,x,y,w;
bool shit(){
    int s=0;
    for (int i=1;i<=n/k;i++)
    s+=k*i-(lower_bound(a+(i-1)*k+1,a+i*k+1,make_pair(ans+1-c[i],0))-a)+1;
    if (s>=w) return(1);
    return(0);
}
int main(){
    scanf("%d%d",&n,&m);
    k=200;
    for (int i=1;i<=n;i++){scanf("%d",&a[i].a);a[i].b=i;c[i]=a[i].a;}
    sort(c+1,c+n+1);
    ans=c[(n+1)/2];
    w=(n+1)/2;
    memset(c,0,sizeof(c));
    while (n%k) a[++n].a=0,a[n].b=n;
    for (int i=1;i<=n+1;i++) id[i]=(i-1)/k+1;
    for (int i=1;i<=n/k;i++) sort(a+k*(i-1)+1,a+k*i+1);
    for (int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        for (int j=id[x-1]+1;j<=id[y+1]-1;j++)c[j]++;
        t=id[x];
        if ((x-1)%k!=0){
            for (int j=(t-1)*k+1;j<=t*k;j++)
                if (a[j].b>=x&&a[j].b<=y) a[j].a++;
            sort(a+(t-1)*k+1,a+t*k+1);
        }
        t=id[y];
        if (y%k!=0&&id[x-1]!=id[y+1]){
            for (int j=(t-1)*k+1;j<=t*k;j++)
                if (a[j].b<=y) a[j].a++;
            sort(a+(t-1)*k+1,a+t*k+1);
        }
        if (shit()) ans++;
        printf("%d\n",ans);
    }
}

T3

此题及其恶心(因为李日天大神不会做)

所以我就弃坑了→_→

Category: 未分类 | Tags:
12
20
2015
0

Contest20151211滚粗记

又是一场JSOI,本蒟蒻再次滚粗。。

T1

看数据像是n算法,然而我只会暴力。。

正解是压位+拓扑。

删掉一条u->v的边的条件是已有u到v的路径。

于是只要每点的连通性即可。

 

#include<cstdio>
#include<bitset>
using namespace std;
#define N 31000
#define M 101000
bitset<31000> f[N],g[N]; 
int n,m,k,l,t,s,ans,num,pre,tail;
int flag[N],head[M],vet[M],next[M],d[N],x[M],y[M],q[N];
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        scanf("%d%d",&x[i],&y[i]);
        next[++num]=head[x[i]];
        head[x[i]]=num;
        vet[num]=y[i];
        d[y[i]]++;
    }
    for (int i=1;i<=n;i++) f[i][i]=g[i][i]=1;
    for (int i=1;i<=n;i++) if (d[i]==0) q[++tail]=i;
    while (tail!=pre){
        int u=q[++pre];
        for (int e=head[u];e;e=next[e]){
            int v=vet[e];
            d[v]--;
            if (d[v]==0) q[++tail]=v;
        }
    }
    for (int i=n;i>0;i--){
        int u=q[i];
        for (int e=head[u];e;e=next[e]) f[u]|=f[vet[e]];
    }
    for (int i=1;i<=n;i++){
        int u=q[i];
        for (int e=head[u];e;e=next[e]) g[vet[e]]|=g[u];
    }
    for (int i=1;i<=m;i++)
        if ((g[y[i]]&f[x[i]]).count()>2) ans++;
    printf("%d\n",ans); 
}

T2

orz Gold_7 AC

正解网络流最小割。

每个方格为一个点,相邻两点边权为之间造墙的代价,正权点连源点,负权点连汇点。

之后跑一遍网络流就搞定辣

 

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define N 310
#define M 410000
int num,n,m,k,l,t,x,y,u,v,ans,s;
int head[M],next[M],vet[M],cost[M],id[N][N],gap[M],dis[M];
void add(int a,int b,int c){
    next[++num]=head[a];
    head[a]=num;
    vet[num]=b;
    cost[num]=c;
    next[++num]=head[b];
    head[b]=num;
    vet[num]=a;
    cost[num]=0;
}
int dfs(int u,int aug){
    if (u==t) return(aug);
    int flow=0,mi=t+1;
    for (int e=head[u];e;e=next[e]){
        int v=vet[e];
        if (!cost[e]) continue;
        if (dis[u]==dis[v]+1){
            int tt=dfs(v,min(aug-flow,cost[e]));
            cost[e]-=tt;
            if (e&1) cost[e+1]+=tt;
            else cost[e-1]+=tt;
            flow+=tt;
            if (dis[s]>=t+1) return flow;
            if (flow==aug) break;
        }
        mi=min(mi,dis[v]);
    }
    if (!flow){
        gap[dis[u]]--;
        if (!gap[dis[u]]) dis[s]=t+1;
        dis[u]=mi+1;
        gap[dis[u]]++;
    }
    return flow;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            id[i][j]=(i-1)*m+j;
            scanf("%d",&x);
            if (x>0) {add(0,id[i][j],x);ans+=x;};
            if (x<0) {add(id[i][j],n*m+1,-x);ans-=x;}
        }
    for (int i=1;i<n;i++)
        for (int j=1;j<=m;j++){
            scanf("%d",&x);
            u=id[i][j];v=id[i+1][j];
            add(u,v,x);
            add(v,u,x);
        }
    for (int i=1;i<=n;i++)
        for (int j=1;j<m;j++){
            scanf("%d",&x);
            u=id[i][j];v=id[i][j+1];
            add(u,v,x);
            add(v,u,x);
        }
    t=n*m+1;
    s=0;
    gap[0]=t+1;
    while (dis[s]<t+1) ans-=dfs(s,1000000);
    printf("%d\n",ans);
}

T3

显然最长的len为,然后二分最大值。

每个点能扩展len位就扩展len位,否则扩展len-1位。

然而听说要用后缀数组维护。。

于是。。果断放弃此题→_→

Category: 未分类 | Tags:
12
19
2015
0

Contest20151217滚粗记

又是一场JSOI(JS没事考那么多场干嘛,闲的蛋疼吗)

T1

看起来很水啊,似乎贪心就可以了嘛。

然而,一秒后发现反例……

再一看似乎是DP,不过是哪种类型呢。。好像是邮局dp(pol取的名字)

一分钟后又发现反例。。

然而这是一道区间dp。

首先把每个敌机抽象为一条a[i]-b[i]的线段,问题便转化为切断所有线段的最小代价。

对于区间[l,r],找到包含在区间内的代价最大的线段,枚举上面的点进行切割,然后分成两个小区间,这样就可以转移辣~

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 610
int a[N],b[N],c[N],p[N],d[N],f[11000],dp[N][N];
bool cmp(int x,int y){
    return d[x]<d[y];
}
int r,n,m,k,l,t,s,cnt,cas;
int main(){
    memset(f,0,sizeof(f));
    scanf("%d",&n);
    cnt=0;
    for (int i=1;i<=n;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]);
    for (int i=1;i<=n;i++){d[++cnt]=a[i];d[++cnt]=b[i];p[i*2]=i*2;p[i*2-1]=i*2-1;}
    sort(p+1,p+2*n+1,cmp);
    cnt=0;
    for (int i=1;i<=2*n;i++){
        if (d[p[i]]!=d[p[i-1]]) cnt++;
        f[d[p[i]]]=cnt;
    }
    memset(dp,10,sizeof(dp));
    for (int i=1;i<=cnt+1;i++) dp[i][i-1]=0;
    for (int t=1;t<=cnt;t++)
        for (int i=1;i<=cnt-t+1;i++){
            int j=i+t-1;
            int mx=0;
            for (int k=1;k<=n;k++)
                if ((f[a[k]]>=i)&&(f[b[k]]<=j))
                    if (c[k]>mx) {mx=c[k];l=k;}
            if (mx==0) dp[i][j]=0;
            for (int k=f[a[l]];k<=f[b[l]];k++)
                dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+mx);
        }
    printf("%d\n",dp[1][cnt]);
}

T2

此乃狗题(因为连gonens大神都不会做)

目前只有60分算法,对于每个点做dfs,线段树维护根到当前点有哪些边,统计答案即可。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 50010
#define M 1000010
int head[N],vet[N],cost[N],next[N],tree[4*M];
int num,n,m,k,l,t,x,y,z;
double s,ans;
void add(int a,int b,int c){
	next[++num]=head[a];
	head[a]=num;
	vet[num]=b;
	cost[num]=c;
}
void change(int l,int r,int x,int p,int vv){
	if (l==r){
		tree[p]+=vv;
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) change(l,mid,x,p+p,vv);
	else change(mid+1,r,x,p+p+1,vv);
	tree[p]=tree[p+p]+tree[p+p+1];
}
int find(int l,int r,int x,int p){
	if (l==r){
		return(l);
	}
	int mid=(l+r)/2;
	if (tree[p+p]>=x) return(find(l,mid,x,p+p));
	else return(find(mid+1,r,x-tree[p+p],p+p+1));
}
void dfs(int u,int fa,int len){
	if ((len%2)==1){k++;s+=find(1,1000000,(len+1)/2,1);}
	for (int e=head[u];e;e=next[e]){
		int v=vet[e];
		if (v!=fa){
			change(1,1000000,cost[e],1,1);
			dfs(v,u,len+1);
			change(1,1000000,cost[e],1,-1);
		}
	}
}
int main(){
	freopen("random.in","r",stdin);
	freopen("random.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	for (int i=1;i<=n;i++){
		dfs(i,0,0);
	}
	ans=(double)(s/k);
	printf("%.7lf\n",ans);
}

T3

看着数据就感觉应该要转换成离线(否则不是人能做的)

没错正解就是转成离线→_→

排序后按边权从大到小加边,如果该边的两点已经连通,则删去两点间最长的边。

对每个前缀开可持久化线段树维护

询问[l,r]的答案为编号为l的数的前缀r。

 

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
#define N 1000010
#define M 1000100
struct node{int l,r,s;}T[6000000];
int f[N],a[N],b[N],c[N],root[N],head[N],next[M],cost[M],vet[M],map[M],tid[M];
int num,n,m,k,l,t,s,ans,q,r,f1,f2,id,shit,x,y;
void add(int a,int b,int c,int d){
    next[++num]=head[a];
    head[a]=num;
    vet[num]=b;
    cost[num]=c;
    tid[num]=d;
    next[++num]=head[b];
    head[b]=num;
    vet[num]=a;
    cost[num]=c;
    tid[num]=d;
}
void sort(int l,int r){
    int i=l;
    int j=r;
    int x=c[(l+r)/2];
    while (i<j){
        while (c[i]<x) i++;
        while (x<c[j]) j--;
        if (i<=j){
            t=a[i];a[i]=a[j];a[j]=t;
            t=b[i];b[i]=b[j];b[j]=t;
            t=c[i];c[i]=c[j];c[j]=t;
            i++;
            j--;
        }
    }
    if (l<j) sort(l,j);
    if (i<r) sort(i,r);
}
int find(int x){
    if (f[x]!=x) f[x]=find(f[x]);
    return(f[x]);
}
void ins(int &i,int l,int r,int x,int v){
    T[++id]=T[i];i=id;T[i].s+=v;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) ins(T[i].l,l,mid,x,v);
    else ins(T[i].r,mid+1,r,x,v);
}
int query(int i,int l,int r,int x){
    if ((1<=x)&&(x>=r)) return(T[i].s);
    int mid=(l+r)>>1;
    if (x<=mid) return(query(T[i].l,l,mid,x));
    else return(T[T[i].l].s+query(T[i].r,mid+1,r,x));
}
int dfs(int u,int fa,int st,int l){
    if (u==st){
        t=l;
        return(1);
    }
    int p=l;
    for (int e=head[u];e;e=next[e]){
        int v=vet[e];
        if (v!=fa){
            p=l;
            if ((c[map[l]]<c[map[tid[e]]])||(l==-1)) p=tid[e];
            if (dfs(v,u,st,p)) return(1);
        }
    }
    return(0);
}
int main(){
    scanf("%d%d",&n,&m);
    id=0;
    for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
    sort(1,m);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int w=m;w>0;w--){
        root[w]=root[w+1];
        if (a[w]==b[w]) continue;
        f1=find(a[w]);
        f2=find(b[w]);
        if (f1!=f2){
            f[f2]=f1;
            map[++map[0]]=w;
            ins(root[w],1,m,w,c[w]);
        }else{
            int mx=0;
            for (int i=1;i<=n;i++) head[i]=0; 
            num=0;
            for (int i=1;i<=map[0];i++) add(a[map[i]],b[map[i]],c[map[i]],i);
            dfs(a[w],0,b[w],-1);
//          printf("-%d\n",map[t]);
            ins(root[w],1,m,map[t],-c[map[t]]);
            for (int i=t;i<map[0];i++) map[i]=map[i+1];
            map[map[0]]=w;
            ins(root[w],1,m,w,c[w]);
        }
    }
//  return 0;
    int ans=0;
    scanf("%d%d",&q,&shit);
    for (int i=1;i<=q;i++){
        scanf("%d%d",&x,&y);
        l=ans*shit+x;
        r=l+y;
        l=lower_bound(c+1,c+1+m,l)-c;
        r=upper_bound(c+1,c+1+m,r)-c-1;
        if (l>r) ans=0;
        else ans=query(root[l],1,m,r);
        printf("%d\n",ans);
    }
}
Category: 未分类 | Tags:
12
16
2015
0

Contest20151214滚粗记

又膜了场JSOI2015,壮烈垫底

条哥不知道去哪浪,先把题目放上了

为了比赛的公平性,我先陪神(S)犇(B)桢泼会儿隔膜

T1

看上去好像是DP,但不知道怎么转移。。

orz NiroBC AC

orz 神(S)犇(S)桢 40

正解是容斥,答案为:

三维枚举i,j,k套下公式就搞定辣。

 

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 405
#define P 1000000007
int n,m,t,k,l;
long long ans;
long long c[N][N],d[N][N*N];
int main(){
    scanf("%d%d%d",&n,&m,&l);
    for (int i=0;i<=400;i++) c[i][0]=1;
    c[1][1]=1;
    for (int i=1;i<=max(n,max(m,l));i++)
        for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
    for (int i=1;i<=401;i++) d[i][0]=1;
    for (int i=1;i<=l+1;i++) 
        for (int j=1;j<=n*m;j++) d[i][j]=(d[i][j-1]*i)%P;
    for (int i=0;i<=n;i++)
        for (int j=0;j<=m;j++)
            for (int k=0;k<=l;k++){
                long long s=c[n][i]*c[m][j]%P*c[l][k]%P*d[k+1][i*j]%P;
                if ((n+m+l-i-j-k)%2==0) ans=(ans+s)%P;else ans=(ans-s+P)%P;
            }
    printf("%lld\n",ans);
}

T2

这是一道水题(好吧其实是以前做过)

维护一个队列,里面存前面产生的gcd及产生该gcd的最左端。

易得队列有单调性,简单维护即可。

理论复杂度为n2,但实际复杂度较小(有很多gcd重复)。

然而。。有个变量没开long long然后滚粗了。。

 

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1001000
long long a[N],b[N],c[N];
int n,m,t;
long long k,l,s,ans;
long long gcd(long long a,long long b){
    if (a%b) return(gcd(b,a%b));
    else return(b);
}
int main(){
//  freopen("gcd.in","r",stdin);
//  freopen("gcd.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for (int i=1;i<=n;i++){
        t=m;
        m=0;
        for (int j=1;j<=t;j++){
            k=gcd(b[j],a[i]);
            if (k!=b[m]){
                b[++m]=k;
                c[m]=c[j];
                ans=max(ans,b[m]*(i-c[m]+1));
            }
        }
        if (b[m]!=a[i]){
            b[++m]=a[i];
            c[m]=i;
            ans=max(ans,a[i]);
        }
    }
    printf("%lld\n",ans);
}

T3

一道十分复杂的题目(好吧其实是我懒。。)

首先对每条路径经过点新开一个点,上站的边权为1,下站和中途乘坐边权为0。

然后跑一遍最短路就求出第一问辣(SPFA会被卡飞,只能用堆优化dijkstra)。

然后处理出所有可能经过的点建新图(可以保证是有向无环图)。

按拓扑序求出最长距离就可以辣。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#define a first
#define b second
#define N 100100
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int> PII;
const int M=5000010;
const int inf=2000000000;
 
pair<ull,int>a[M];
int n,m,y,t,st,ed,tmp,cnt,num,Num;
char s[50];
int head[M],vet[M],next[M],Head[M],Vet[M],Next[M],cost[M],init[M],dis[M],Dis[M],len[M][2],flag[M],x[M],q[M];
priority_queue<PII,vector<PII>,greater<PII> >Q;
int shit(){
    ull w=0;
    for (int i=0;i<strlen(s);i++) w=w*233+s[i];
    return(w);
}
int solve(){
    ull ss=shit();
    int ans=a[lower_bound(a+1,a+n+1,make_pair(ss,0))-a].b;
    return(ans);
}
void add(int a,int b,int c,int d){
    next[++num]=head[a];
    head[a]=num;
    vet[num]=b;
    len[num][0]=c;
    len[num][1]=d;
}
void Add(int a,int b,int c){
    Next[++Num]=Head[a];
    Head[a]=Num;
    Vet[Num]=b;
    cost[Num]=c;
    init[b]++;
}
int main(){
    scanf("%d%d",&m,&n);
    for (int i=1;i<=n;i++){
        scanf("%s",s);
        for (int j=0;j<strlen(s);j++)a[i].a=shit();
        a[i].b=i;
    }
    sort(a+1,a+n+1);
    cnt=n;
    for (int i=1;i<=m;i++){
        scanf("%d",&t);
        scanf("%s",s);
        x[1]=solve();
        add(x[1],++cnt,1,0);
        for (int j=2;j<=t;j++){
            scanf("%s",s);
            y=cnt,x[j]=solve();
            add(y,++cnt,0,1);
            add(x[j],cnt,1,0);
            add(cnt,x[j],0,0);
        }
        add(x[t],++cnt,1,0);
        for (int j=t-1;j>=1;j--){
            y=cnt;
            add(y,++cnt,0,1);
            add(x[j],cnt,1,0);
            add(cnt,x[j],0,0);
        }
    }
    scanf("%s",s);
    st=solve();
    scanf("%s",s);
    ed=solve();
    for (int i=1;i<=cnt;i++) dis[i]=inf;
    for (int i=1;i<=cnt;i++) flag[i]=0;
    Q.push(make_pair(0,st));dis[st]=0;
    while (!Q.empty()){
        int u=Q.top().b;Q.pop();
        if (flag[u])continue;
        flag[u]=1;
        for (int e=head[u];e;e=next[e]){
            int v=vet[e];
            if (dis[u]+len[e][0]<dis[v]){
                dis[v]=dis[u]+len[e][0];
                Q.push(make_pair(dis[v],v));
            }
        }
    }  
    for (int i=1;i<=cnt;i++)
        for (int e=head[i];e;e=next[e]){
            if (dis[i]+len[e][0]==dis[vet[e]]) Add(i,vet[e],len[e][1]);
        }
    int pre=1,tail=0;
    for (int i=1;i<=cnt;i++) if (init[i]==0) q[++tail]=i;
    while (pre<=tail){
        int u=q[pre++];
        for (int e=Head[u];e;e=Next[e]){
            int v=Vet[e];
            Dis[v]=max(Dis[v],Dis[u]+cost[e]);
            if ((--init[v])==0) q[++tail]=v;
        }
    }
    printf("%d\n",dis[ed]);
    printf("%d\n",Dis[ed]);
}
Category: 未分类 | Tags:
12
9
2015
0

Contest20150207滚粗记

又膜了场JSOI2015,壮烈爆蛋

听说lyz大神(S)犇(B)在嘲笑我,这不能忍啊。。我改5个字符就可以轻松碾压他→_→

T1

看到期望吓尿了,先蹭点薯片压压惊。

考完才发现,这™是道水(SB)题。。

先预处理出每个妹子选各个汉子的概率,直接套等比数列求和公式:

然后用树状数组求和就OK辣~

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 510000
#define a(i) a[(i)].a
#define b(i) a[(i)].b
using namespace std;
struct node{
	int a,b;
};
bool cmp(node x, node y){
    if(x.a!=y.a) return x.a<y.a;
    else return x.b<y.b;
}
double ans,s,p;
double tr[N],p1[N],p2[N],f[N];
int d[N];
node a[N];
int n,m,t,l,k;
int main(){
	scanf("%d%d",&n,&m);
	scanf("%lf",&p);
	for (int i=1;i<=n;i++) scanf("%d%d",&a(i),&b(i));
	p1[0]=p2[0]=1;
	for (int i=1;i<=n;i++){p1[i]=p1[i-1]*p;p2[i]=p2[i-1]*(1-p);}
	sort(a+1,a+n+1,cmp);
	for (int i=1;i<=n;i++) d[a(i)]++;
	for (int i=1;i<=n;i++){
		if (a(i)!=a(i-1)) l=0;
		f[i]=(p2[l++]*p)/(1-p2[d[a(i)]]);
	}
	for (int i=n;i>0;i--){
		k=b(i)-1;
		while (k>0){
			t=k&(-k);
			ans+=f[i]*tr[k];
			k-=t;
		}
		k=b(i);
		while (k<=n){
			if (!k) break;
			tr[k]+=f[i];
			k+=k&(-k);
		}
	}
	printf("%.2lf\n",ans);
}

T2

看起来很水的样子,数据明显是让你nlogn嘛,先让我按out排个序。

诶好像是贪心,蛤蛤线段树维护一下。

呀好像in会冲突,离散一下~快排stl不会用。。那就手打吧= =

woc细节好多。。打了两小时终于搞定了耶。

然而……爆蛋了……

日,线段树打错了

 

#include<cstdio>
#include<cmath>
#define N 210000
using namespace std;
int n,m,s,t,k,l;long long ans;
int num[N*4],tree[N*4],a[N],b[N],d[N],in[N],out[N],p[N],pre[N];
long long c[N];
void qsort(int l,int r)
{
	int i,j,x,y;
	i=l;
	j=r;
	x=a[(l+r)/2];
	while  (i<j){
    	while (a[i]<x) i++;
    	while (x<a[j]) j--;
    	if (i<=j) {
        	y=a[i];a[i]=a[j];a[j]=y;
        	y=p[i];p[i]=p[j];p[j]=y;
        	i++;
        	j--;
      	}
    }
  if (l<j) qsort(l,j);
  if (i<r) qsort(i,r);
}
void qsort2(int l,int r)
{
	int i,j,x,y;
	i=l;
	j=r;
	x=out[(l+r)/2];
	while  (i<j){
    	while (out[i]>x) i++;
    	while (x>out[j]) j--;
    	if (i<=j) {
        	y=out[i];out[i]=out[j];out[j]=y;
        	y=in[i];in[i]=in[j];in[j]=y;
        	i++;
        	j--;
      	}
    }
  if (l<j) qsort2(l,j);
  if (i<r) qsort2(i,r);
}
void add(int l,int r,int x,int p,int v){
	if ((x<1)||(x>n)) return;
	if (l==r){
		num[p]=v;
		tree[p]=d[v];
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) add(l,mid,x,p+p,v);
	else add(mid+1,r,x,p+p+1,v);
	if (tree[p+p]>tree[p+p+1]){
		tree[p]=tree[p+p];
		num[p]=num[p+p];
	}
	else {
		tree[p]=tree[p+p+1];
		num[p]=num[p+p+1];
	}
}
int find(int l,int r,int x,int y,int p){
	if (x>y) return 0;
	if ((l==x)&&(r==y)){
		return(num[p]);
	}
	int mid=(l+r)/2;
	int k,t1,t2;
	if (y<=mid) return(find(l,mid,x,y,p+p));
	if (x>mid) return(find(mid+1,r,x,y,p+p+1));
	if ((x<=mid)&&(y>mid)){
		t1=find(l,mid,x,mid,p+p);
		t2=find(mid+1,r,mid+1,y,p+p+1);
		if (d[t1]>d[t2]) return(t1);
		else return(t2);
	}
}
int main()
{
//	freopen("doll.in","r",stdin);
//	freopen("doll.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d%d%d",&out[i],&a[i],&b[i]);
	for (int i=1;i<=n;i++) p[i]=i;
	qsort(1,n);
	l=1;
	for (int i=1;i<=n;i++){
		if (a[i]!=a[i-1]) {
			pre[a[i]]=i;
			for (int j=l;j<a[i];j++) pre[j]=i;
			l=a[i]+1;
		}
		in[p[i]]=i;
		d[i]=b[p[i]];
		c[i]=a[i];
	}
	for (int i=l;i<=10000;i++) pre[i]=n+1;
	qsort2(1,n);
	for (int i=1;i<=n;i++){
//		printf("%d\n",out[i]);
		if (out[i]==10000) l=0;
		else l=find(1,n,pre[out[i]+1],n,1);
		c[l]-=out[i];
		add(1,n,l,1,0);
		add(1,n,in[i],1,in[i]);
	}
	for (int i=1;i<=n;i++) ans+=c[i]*d[i];
	printf("%lld\n",ans);
}

T3

看到计算几何吓傻了,果断放弃。。

听说小天才lyk乱搞A了orz

此题没有正解,尽情乱搞吧

然而我是拒绝做这种码农题的→_→

Category: 未分类 | Tags:
12
2
2015
0

Contest20151130滚粗记

听说这是JSOI2015Day2

本蒟蒻再次滚粗。。

Orz NiroBC 230

Orz Gold_7 220

Orz Chrysanthemum 220

本蒟蒻2、3两题炸飞,100收场

 

T1 

表示被题目吓到了,模拟了下样例,还是看不出什么。。

此时桢神(S)犇(B)吼道:这是傻逼题啊卧槽。。

貌似是结论题,窝这种蒟蒻只好猜结论了→_→

好像是答案是2nk。。嗯过大样例了。。

#include<cstdio>
#define p 1000000007
long long ans,n,m,k;
long long mi(long long x,long long y)
{
	long long mi=1;
	while (y>0){
		if (y%2==1) mi=mi*x%p;
		y/=2;
		x=x*x%p;
	}
	return(mi);
}
int main()
{
	freopen("subset.in","r",stdin);
	freopen("subset.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	k=mi(2,n);
    ans=mi(k,m);
    printf("%lld\n",ans);
	
}

T2

这个K真是不明觉厉。。

打个20分暴力吧。。

诶爆0了。。原来是没写using namespace std; (刚转C勿喷)

正解是二分

如果存在更优解,则有

对于len=l的情况,单调队列维护取最优解即可。

对于len∈(l,r],最优解必为M(i,j)=a[i],m(i,j)=a[j]或M(i,j)=a[j],m(i,j)=a[i]。

那么将分母乘过去,就有:a[i]+b*i-(a[j]+b*j)>b*K或a[j]-b*j-(a[i]-b*i)>b*K。

然后两个单调队列维护即可。

 

#include<cstdio>
int n,m,k,cas,l,r,head,tail,h,t;
int q[100000],p[100000],a[100000];
double ans,ans2;
double erfen(double x,double y)
{
    if (y-x<1e-6) return(x);
    double z=(x+y)/2;
    head=1;tail=0;q[1]=0;
     
    for (int i=l;i<=n;i++){
        if (i>l){
            while ((q[head]<=i-r)&&(head<tail)) head++;
            if (a[q[head]]+z*q[head]-a[i]-i*z>z*k) return(erfen(z,y));
        }
        int m=i-l+1;
        while ((a[m]+m*z>a[q[tail]]+z*q[tail])&&(tail>=head)) tail--;
        q[++tail]=m;
    }
    head=1;tail=0;q[1]=0;
    for (int i=l;i<=n;i++){
        if (i>l){
            while ((q[head]<=i-r)&&(head<tail)) head++;
            if (-a[q[head]]+z*q[head]+a[i]-i*z>z*k) return(erfen(z,y));
        }
        int m=i-l+1;
        while ((a[m]-m*z<a[q[tail]]-z*q[tail])&&(tail>=head)) tail--;
        q[++tail]=m;
    }
    return(erfen(x,z));
}
int main()
{
    scanf("%d",&cas);
    while (cas-->0)
    {
        scanf("%d%d%d%d",&n,&k,&l,&r);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        ans=0;
        ans=erfen(0,1000);
        head=1;tail=0;q[1]=0;
        h=1;t=0;p[1]=0;
        ans2=0;
        for (int i=1;i<=n;i++){
            while ((a[i]>a[q[tail]])&&(tail>=head)) tail--;
            q[++tail]=i;
            while ((a[i]<a[p[t]])&&(t>=h)) t--;
            p[++t]=i;
            if (i>=l){
                while ((q[head]<=i-l)&&(head<tail)) head++;
                while ((p[h]<=i-l)&&(h<t)) h++;
                if ((double)(a[q[head]]-a[p[h]])/(l-1+k)>ans2) ans2=(double)(a[q[head]]-a[p[h]])/(l-1+k);
            }
        }
        if (ans>ans2) printf("%.4lf\n",ans);
        else printf("%.4lf\n",ans2);
    }
}

T3

听说是可持久化trie树,然而我脑补了个hash做法。

l-r的前缀个数为根节点到l的个数+根节点到r的个数-2*根节点到lca(l,r)的个数。只要dfs时更新hash数组P,可O(1)询问。

注意hash冲突,概率蛮大的。。窝写了三个hash才过。。

 

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 310000
#define P 19999999
#define P1 19999981
#define P2 19999963
#define M 211
int nu,x,y,n,m,num,k,s,l;
long long w;
int e[N],d[N],c[N],ans[N],head2[N],next2[N],vet2[N],flag[N],f[N],lca[N],head[N],next[N],vet[N],head1[N],next1[N],vet1[N],cost1[N],p2[21000000],p[21000000],p1[21000000];
int cost[N][11][3],cost2[N][3];
char ch[11];
 
int add(int a,int b)
{
    next[++num]=head[a];
    head[a]=num;
    vet[num]=b;
}
int addd(int a,int b,int c)
{
    next1[++num]=head1[a];
    head1[a]=num;
    vet1[num]=b;
    cost1[num]=c;
}
int ad(int a,int b,int c,int d,int e)
{
    next2[++nu]=head2[a];
    head2[a]=nu;
    vet2[nu]=b;
    cost2[nu][0]=c;
    cost2[nu][1]=d;
    cost2[nu][2]=e;
}
int find(int x)
{
    if (f[x]!=x) f[x]=find(f[x]);
    return(f[x]);
}
int df(int u,int fa)
{
    int v=0;
    flag[u]=1;
    for (int e=head1[u];e!=0;e=next1[e]){
        v=vet1[e];
        if (flag[v]=1) lca[cost1[e]]=v;
        if (flag[v]=2) lca[cost1[e]]=find(v);
    }
    for (int e=head[u];e!=0;e=next[e]){
        v=vet[e];
        if (v!=fa) df(v,u);
    }
    f[u]=fa;
    flag[u]=2;
}
int dfs(int u,int fa)
{
    int v=0;
    int shit;
    for (int e=head2[u];e!=0;e=next2[e]){
        v=vet2[e];
        if (e<=2*m) shit=1;
        else shit=-2;
        if (cost2[e]!=0)
        ans[v]=ans[v]+shit*min(min(p[cost2[e][0]],p1[cost2[e][1]]),p2[cost2[e][2]]);
    }   
    for (int e=head[u];e!=0;e=next[e]){
        v=vet[e];
        if (v!=fa){
            for (int i=0;i<=9;i++) {p[cost[e][i][0]]++;p1[cost[e][i][1]]++;p2[cost[e][i][2]]++;}
            dfs(v,u);
            for (int i=0;i<=9;i++) {p[cost[e][i][0]]--;p1[cost[e][i][1]]--;p2[cost[e][i][2]]--;}
        }
    }
}   
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;++i){
        scanf("%d%d%s",&x,&y,ch);
        add(x,y);
        add(y,x);
        w=0;
        long long ww=0;
        long long w2=0;
        for (int j=0;j<=9;j++){
            if (ch[j]<97) break;
            w=(w*M+ch[j])%P;
            ww=(ww*M+ch[j])%P1;
            w2=(w2*M+ch[j])%P2;
            cost[num-1][j][0]=cost[num][j][0]=w;
            cost[num-1][j][1]=cost[num][j][1]=ww;
            cost[num-1][j][2]=cost[num][j][2]=w2;
        }
    }
    num=0;
    for (int i=1;i<=n;i++) f[i]=i;
    scanf("%d",&m);
    for (int i=1;i<=m;i++){
        scanf("%d%d%s",&x,&y,ch);
        w=0;
        long long ww=0;
        long long w2=0;
        for (int j=0;j<=9;j++){
            if (ch[j]<97) break;
            w=(w*M+ch[j])%P;
            ww=(ww*M+ch[j])%P1;
            w2=(w2*M+ch[j])%P2;
        }
        c[i]=w;
        d[i]=ww;
        e[i]=w2;
        addd(x,y,i);
        ad(x,i,c[i],d[i],e[i]);
        ad(y,i,c[i],d[i],e[i]);
        addd(y,x,i);
    }
    df(1,0);
    for (int i=1;i<=m;i++) ad(lca[i],i,c[i],d[i],e[i]);
    dfs(1,0);
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);    
}
Category: 滚粗记 | Tags:
11
28
2015
0

Contest20151123滚粗记

 

pol放了套JSOI day1原题。。

本蒟蒻表示不明觉厉= =

T1  树形dp

看完描述顿时慌了,ZJOI day2的即视感orz。。再看输入格式,woc原来这是一棵su!!那不成水题了么。。

当时正在转C,进度仅30%。。打C没把握,只好默默转回P(转C转了一星期还没转完,仅一秒就转回了P……)。40分钟AC。笑看lyz、cgt两神(S)犇(B)自信写C然后滚粗→_→

题解:

可经过次数就是这个点能连接的子节点个数,所以取最优值大于0的最优的若干个子节点与之相连便可(sort+线扫)。

对于方案是否唯一,会有以下三种情况:

1、某个儿子的方案不唯一。

2、存在一个未选过的儿子,其最优值与某个选过的儿子的最优值相等。

3、有多余的可连边且儿子中有最优值为0的点。

---------------------------------------------------------------

T2  hash

码农题。膜衲姐hash 90分。

表示n^5懒得打。。(毕竟懒癌晚期……)

正解:

预处理出翻转、对称后的6个矩阵并求出hash值,枚举中心+二分,时间复杂度O(n^2logn)。取模可用unsigned int自然溢出,以防卡常。

---------------------------------------------------------------

T3  拓扑+hash

对同构表示不明觉厉。。

正解:

对于每棵su,先dfs去掉所有假节点,然后在新su上求hash值。

叶子节点的值为1,其余点的值为其叶子节点排序后hash出来的值。遍历可按照拓扑序。这样可以保证同构的su的hash值计算顺序相同。

标程见http://yyhslyz.is-programmer.com/posts/189446.html

Category: 未分类 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com