【NOIP2015TG】solution

链接:https://www.luogu.org/problem/lists?name=&orderitem=pid&tag=83%2C32

D1T1(magic)

题意:看题目。。

解题思路:纯模拟。。。

#include<stdio.h>
short f[40][40],n;
int main()
{
    scanf("%d",&n);
    int x=1,y=n+1>>1;f[x][y]=1;
    for(int i=2; i<=n*n; ++i){
        if(x==1&&y<n)  x=n,++y;
        else if(x>1&&y==n)  x--,y=1;
            else if(x==1&&y==n)  x++;     
                else if(!f[x-1][y+1])x--,y++;
                    else x++;
        f[x][y]=i;
    }
    for(int i=1; i<=n; i++){
        printf("%d",f[i][1]);
        for(int j=2; j<=n; j++)printf(" %d",f[i][j]);
        putchar('\n');
    }
    return 0;
}

 D1T2(message)

题意:给你张图,找最小环。

解题思路:拓扑以后跑环即可。

#include <stdio.h>
#include<string.h>
#define MN 200005
#define inf 0x7fffffff
char B[1<<26],*S=B;
inline int in(){
    for (;*S<'0'||*S>'9';S++);
    register int x=(*S++)-'0';
    for (;*S>='0'&&*S<='9'; x=(x<<3)+(x<<1)+(*S++)-'0');
    return x;
}
int r[MN],to[MN],que[MN],n,ans=inf,h,t;
void init(){fread(B,1,1<<26,stdin);n=in();for (int i=1; i<=n; ++i) to[i]=in(),++r[to[i]];}
inline void dfs(int s){
    memset(que,0,sizeof(que));
    register int cnt=1;que[s]=1;--r[s];
    while(!que[to[s]]) 
        ++cnt,--r[s=to[s]],que[s]=1;;
    ans=cnt<ans?cnt:ans;
}
void solve(){
    for (register int i=1; i<=n; ++i) if (!r[i]) que[++t]=i;
    while(h<t){register int u=que[++h];--r[to[u]];if (!r[to[u]]) que[++t]=to[u];}
    for (register int i=1; i<=n; ++i) if (r[i]) dfs(i);
    printf("%d",ans);
}
int main(){init();solve();return 0;}

D1T3(landlords)

题意:就是简化版的斗地主。

解题思路:典型码农题,我们考虑dfs枚举各种顺的情况,然后贪心计算剩余的没有顺子的最小步数即可。

#include<stdio.h>
#include<string.h>
#define inf 0x7fffffff
int s[16],a[5],ans,n,T;
inline int min(int a,int b){return a<b?a:b;}
inline int in(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline int calc(){
    register int cnt=0;
    a[1]=a[2]=a[3]=a[4]=0;
    for (register int i=1; i<=15; ++i) ++a[s[i]];
    if (s[14]&&s[15]) ++cnt,a[1]-=2;
    while(a[4]&&a[1]>1) ++cnt,--a[4],a[1]-=2;
    while(a[4]&&a[2]>1) ++cnt,--a[4],a[2]-=2;
    while(a[3]&&a[2]) ++cnt,--a[3],--a[2];
    while(a[3]&&a[1]) ++cnt,--a[3],--a[1];
    return cnt+a[4]+a[3]+a[2]+a[1];
}
inline void dfs(int k){
    if (k>=ans) return;
    ans=min(ans,k+calc());
    register int j;
    for (register int i=1; i<12; ++i){
        j=i;
        while(j<13&&s[j]>2){
            s[j++]-=3;
            if (j-i>=2) dfs(k+1);
        }
        while(j-i)s[--j]+=3;
    }
    for (register int i=1; i<11; ++i){
        j=i;
        while(j<13&&s[j]>1){
            s[j++]-=2;
            if (j-i>=3) dfs(k+1);
        }
        while(j-i)s[--j]+=2;
    }
    for (register int i=1; i<9; ++i){
        j=i;
        while(j<13&&s[j]){
            --s[j++];
            if (j-i>=5) dfs(k+1);
        }
        while(j-i)++s[--j];
    }
}
inline int getno(int x){
    register int y=in();
    if (!x) return y+13;
    if (x<3) return x+11;
    return x-2;
}
void work(){
    memset(s,0,sizeof(s));ans=inf;
    for (register int i=1; i<=n; ++i) ++s[getno(in())];
    dfs(0);printf("%d\n",ans);
}
int main(){T=in();n=in();while(T--) work();return 0;}

D2T1(stone)

题意:给你N个石头,问你拿走任意M块之后使得任意2点之间距离最小的最大是多少。

解题思路:正难则反,我们可以考虑给定最小的距离最大的值,如何求出拿走了多少块。显然这样只需要\(O(n)\)扫描一下即可,继续观察对于答案的递增,m会随之递增,故答案满足某种单调性,我们可以考虑二分答案+check解决此题,时间效率\(O(n \lg L)\)。

#include<stdio.h>
#define MN 50005
#define mid (l+r+1>>1)
int L,n,a[MN],m,l,r;
inline int in(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
bool check(int l){
    register int ans=0,x=0;
    for (register int i=1; i<=n; ++i)
        if (a[i]-a[i-1]+x<l) {
            ++ans;x+=a[i]-a[i-1];
            if (ans>m) return 0;
        }
        else x=0;
    return 1;
}
int main(){
    L=in(),n=in(),m=in();
    for (int i=1; i<=n; ++i) a[i]=in();a[++n]=r=L;
    for (; l<r; check(mid)?l=mid:r=mid-1);
    printf("%d",l);
}

D2T2(substring)

题意:给你一个原串A,和一个目标串B,叫你求A中任取K个不相交子串等于B串的方法数。

解题思路:十分难想的DP可以用前缀和优化一下,转移方程见std,理由就不写了,参考各路大神的标程吧。。。

#include<stdio.h>
#define ll long long
#define mod 1000000007
ll f[201][201]={1},sum[201][201];
int n,m,kk;
char a[1001],b[201];
int main(){
    scanf("%d%d%d%s%s",&n,&m,&kk,a,b);
    for(int i=1; i<=n; ++i)
      for(int j=m; j; --j)
        for(int k=kk; k; --k)
              f[j][k]=(f[j][k]+(sum[j][k]=a[i-1]==b[j-1]?sum[j-1][k]+f[j-1][k-1]:0))%mod;
    printf("%d",f[m][kk]);
}

D2T3(transport)

题意:给你一棵有边权的树。你可以任选一条边令其边权为0,现在有m个东西以相同的速度从s[i]移到t[i],问你最后一个结束的时间最小是多少。

解题思路:首先,我们知道,题目给出了棵树,接下来,我们容易发现,对于一个任务,这个任务所需要花费的时间为dis[s]+dis[t]-dis[lca(s,t)]*2,接下来我们可以发现,题意只要求找出最小时间,显然答案是满足单调性的,因此我们可以二分答案,接下来考虑如何check。首先我们容易得知,对于所有任务花费时间大于mid的任务,必然存在一条公共边能够使得最大花费时间任务减去这条公共边的时间能够不大于mid,这样才能够保证mid是可行的,因此我们考虑差分然后O(n)遍历所有边计算出边的使用次数,对于满足公共边条件的边作如上判断即可。时间效率\(O((n+m) \lg ans)\)。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MN 300005
#define mid (l+r>>1)
char B[1<<26],*S=B,C;int X;
inline int in(){
    while((C=*S++)<'0'||C>'9');
    for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0';
    return X;
}
struct zxy{int to,nxt;}lk[MN<<1];
struct e{int to,nxt,v;}edge[MN<<1];
struct plans{int s,t,tim,lca;}plan[MN];
int sum[MN],dis[MN],n,m,cnt,q[MN],lc,h[MN],fa[MN],f[MN],ans,cf[MN];
inline void ins(int x,int y,int v){edge[++cnt].to=y,edge[cnt].nxt=h[x],edge[cnt].v=v,h[x]=cnt;}
inline void insq(int x,int y){lk[++lc].to=y,lk[lc].nxt=q[x],q[x]=lc;}
inline int getfa(int x){return fa[x]?fa[x]=getfa(fa[x]):x;}
inline bool cmp(plans a,plans b){return a.tim>b.tim;}
inline void tjlca(int u,int len){
    dis[u]=len;
    for (register int i=h[u]; i; i=edge[i].nxt)
        if (edge[i].to!=f[u]) f[edge[i].to]=u,tjlca(edge[i].to,len+edge[i].v),fa[edge[i].to]=u;
    for (register int i=q[u]; i; i=lk[i].nxt)
        if (plan[lk[i].to].lca) plan[lk[i].to].lca=getfa(plan[lk[i].to].lca);
        else plan[lk[i].to].lca=u;
}
inline void dfs(int u){
    sum[u]=cf[u];
    for (register int i=h[u]; i; i=edge[i].nxt)
        if (edge[i].to!=f[u]){
            dfs(edge[i].to),sum[u]+=sum[edge[i].to];
        }
}
inline bool check(int max_time){
    if (plan[1].tim>max_time+1000) return 0;
    if (plan[1].tim<=max_time)return 1;
    int cnt,max;cnt=max=0;
    memset(cf,0,sizeof(cf));
    for (register int i=1; i<=m&&plan[i].tim>max_time; ++i) {
        ++cnt;max=plan[i].tim>max?plan[i].tim:max;
        ++cf[plan[i].t],++cf[plan[i].s],cf[plan[i].lca]-=2;
    }dfs(1);int p=0;
    for (register int i=1; i<=n; ++i)
        if (sum[i]==cnt&&dis[i]-dis[f[i]]>dis[p]-dis[f[p]]) p=i;
    if (!p) return 0;
    return max-dis[p]+dis[f[p]]<=max_time;
}
void init(){//checked
    fread(B,1,1<<26,stdin);
    n=in(),m=in();int x,y,v;
    for (int i=1; i<n; ++i){
        x=in(),y=in(),v=in();
        ins(x,y,v);ins(y,x,v);
    }
    for (register int i=1; i<=m; ++i){
        x=in(),y=in();
        plan[i].s=x,plan[i].t=y;
        insq(x,i);insq(y,i);
    }
}
void solve(){
    tjlca(1,0);
    for (register int i=1; i<=m; ++i)
        plan[i].tim=dis[plan[i].s]+dis[plan[i].t]-(dis[plan[i].lca]<<1);
    std::sort(plan+1,plan+m+1,cmp);int l=0,r=300000001;
    for (; l<=r; check(mid)?ans=mid,r=mid-1:l=mid+1);
    printf("%d",ans);
}
int main(){init();solve();return 0;}

 

posted @ 2017-04-01 08:17  Melacau  阅读(168)  评论(0编辑  收藏  举报