山东省第四届ACM省赛

解题报告:http://www.tuicool.com/articles/FnEZJb

A.Rescue The Princess(几何,向量)

B.The number of steps(记忆化搜索,概率dp)

C.Boring Counting(划分树)

D.A-Number and B-Number(数位dp,二分答案)

E.Alice and Bob

F.Mountain Subsequences(dp)

G.Rubik’s cube(哈希+bfs)

H.A^X mod P(转化求幂,打表)

I.Thrall’s Dream(bfs/dfs)

J.Contest Print Server(模拟)

A.Rescue The Princess

d.逆时针给出两个点A、B,求出逆时针构成正三角形的第三个点C。(即A、B、C为逆时针)

s.

法1.

法2.向量旋转公式,证明见:http://www.cnblogs.com/bofengyu/p/5394969.html

c.法1

#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;

const double PI=4*atan(1);

int main(){

    int T;
    double x1,x2,x3;
    double y1,y2,y3;
    double theta;
    double len;

    scanf("%d",&T);

    while(T--){
        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);

        theta=atan2(y2-y1,x2-x1);//不能用atan(),原因是atan不分辨一、三象限,也不分辨二、四象限
        len=sqrt((y2-y1)*(y2-y1)+(x2-x1)*(x2-x1));

        x3=x1+len*cos(theta+PI/3);
        y3=y1+len*sin(theta+PI/3);

        printf("(%.2f,%.2f)
",x3,y3);

    }

    return 0;
}
View Code

c2.法2.向量旋转公式

#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;

const double PI=acos(-1);

int main(){

    int T;
    double x1,x2,x3;
    double y1,y2,y3;
    double sin60=sin(PI/3);
    double cos60=cos(PI/3);

    scanf("%d",&T);

    while(T--){
        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);

        x3=(x2-x1)*cos60-(y2-y1)*sin60+x1;
        y3=(x2-x1)*sin60+(y2-y1)*cos60+y1;

        printf("(%.2f,%.2f)
",x3,y3);

    }

    return 0;
}
View Code

B.The number of steps

C.Boring Counting

s.应该用划分树

c.线段树写的超时了。

/*
线段树
单点更新
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

#define L(root) ((root)<<1)
#define R(root) (((root)<<1)|1)

const int MAXN=50010;//
int numbers[MAXN];//初始值
int A,B;

struct node{
    int left,right;//
    //int sum;
    int ma,mi;
    int mid(){
        return left+((right-left)>>1);
    }
}tree[MAXN*4];//4倍空间

void pushUp(int root){
    //tree[root].sum=tree[L(root)].sum+tree[R(root)].sum;
    tree[root].ma=tree[L(root)].ma>tree[R(root)].ma?tree[L(root)].ma:tree[R(root)].ma;
    tree[root].mi=tree[L(root)].mi<tree[R(root)].mi?tree[L(root)].mi:tree[R(root)].mi;
}

void build(int root,int left,int right){
    tree[root].left=left;
    tree[root].right=right;
    if(left==right){
        //tree[root].sum=numbers[left];
        tree[root].ma=tree[root].mi=numbers[left];
        return;
    }
    int mid=tree[root].mid();
    build(L(root),left,mid);
    build(R(root),mid+1,right);
    pushUp(root);
}

int query(int root,int left,int right){
    if(tree[root].left==left&&tree[root].right==right){
        //return tree[root].sum;
        if(A<=tree[root].mi&&tree[root].ma<=B){
            return right-left+1;
        }
        if(tree[root].left==tree[root].right){
            return 0;
        }
        int mid=tree[root].mid();
        return query(L(root),left,mid)+query(R(root),mid+1,right);
    }
    int mid=tree[root].mid();
    if(right<=mid){
        return query(L(root),left,right);
    }
    else if(left>mid){
        return query(R(root),left,right);
    }
    else{
        return query(L(root),left,mid)+query(R(root),mid+1,right);
    }
}
/*
void update(int root,int pos,int add){
    if(tree[root].left==tree[root].right){
        tree[root].sum+=add;
        return;
    }
    int mid=tree[root].mid();
    if(pos<=mid){
        update(L(root),pos,add);
    }
    else{
        update(R(root),pos,add);
    }
    pushUp(root);
}
*/

int main(){
    /*
    memset(numbers,0,sizeof(numbers));

    int i;
    for(i=1;i<MAXN;++i){
        numbers[i]=i;
    }

    build(1,1,10);

    cout<<query(1,2,3)<<endl;

    update(1,2,100);
    cout<<query(1,2,3)<<endl;
    */

    int T;
    int N,M;
    int L,R;//,A,B;
    int i;
    int Case=0;

    scanf("%d",&T);

    while(T--){
        scanf("%d%d",&N,&M);

        for(i=1;i<=N;++i){
            scanf("%d",&numbers[i]);
        }
        build(1,1,N);

        printf("Case #%d:
",++Case);
        for(i=0;i<M;++i){
            scanf("%d%d%d%d",&L,&R,&A,&B);
            printf("%d
",query(1,L,R));
        }
    }


    return 0;
}
View Code

D.A-Number and B-Number

E.Alice and Bob

F.Mountain Subsequences

G.Rubik’s cube

H.A^X mod P

s.在求A^X幂时,直接快速幂求的话,是O(10^6*log(n)*40)=O(10^9) 肯定会超时。

我们将X转化成x=i*k+j。这样先在数组可以表示的范围内找到一个k,然后保存(A^0)~(A^k)的值,然后再将求出(A^k)^i 保存在数组里,

这样每次求A^x,便可以通过这两个数组在O(1)的时间复杂度内求出来,这样时间复杂度就变成了O(10^6*40) = O(4*10^7)了

#include<iostream>
#include<stdio.h>
using namespace std;

#define ll long long

#define N 31622
#define M 33333

ll powA[N+10];
ll powB[M+10];

ll n,A,K,a,b,m,P;
int cas;

void init(){
    powA[0]=1;
    for(int i=1;i<=N;++i){
        powA[i]=powA[i-1]*A%P;
    }
    ll tmp=powA[N];
    powB[0]=1;
    for (int i=1;i<=M;++i){
        powB[i]=powB[i-1]*tmp%P;
    }
}

void solve(){
    ll fx=K;
    ll ans=0;
    for(int i=1;i<=n;++i){
        ans=(ans+powB[fx/N]*powA[fx%N]%P)%P;
        fx=(a*fx+b)%m;
    }
    printf("Case #%d: %lld
",++cas,ans);
}

int main(){
    int T;
    scanf("%d",&T);

    cas=0;
    while (T--){

        scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&A,&K,&a,&b,&m,&P);
        init();
        solve();

    }

    return 0;
}
View Code

I.Thrall’s Dream

d.这个题意我是着实没大看懂啊。。。英语不好。。

N个点,M条有向边,问这个图中任意两点是否可以到达。

s.直接枚举所有点,bfs或者dfs判断是否可达即可。

c.bfs

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

#define MAXN 2010
#define MAXM 10010

struct Edge{
    int to,next;
}edge[MAXM];
int head[MAXN],tot;

void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}

bool d[MAXN][MAXN];//d[i][j]为true表示i可达j
bool vis[MAXN];

void bfs(int t){
    memset(vis,false,sizeof(vis));

    queue<int>myQueue;
    int u,v;
    int i;

    myQueue.push(t);
    vis[t]=true;

    while(!myQueue.empty()){
        u=myQueue.front();
        myQueue.pop();

        for(i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].to;
            if(!vis[v]){
                d[t][v]=true;
                vis[v]=true;
                myQueue.push(v);
            }
        }

    }
}

int main(){

    int T;
    int N,M;
    int u,v;
    int i,j;
    bool flag;//
    int Case=0;

    scanf("%d",&T);

    while(T--){
        scanf("%d%d",&N,&M);

        init();
        for(i=0;i<M;++i){
            scanf("%d%d",&u,&v);
            addedge(u,v);
        }

        memset(d,false,sizeof(d));
        for(i=1;i<=N;++i){
            bfs(i);
        }

        flag=true;
        for(i=1;i<=N&&flag;++i){
            for(j=1;j<=N&&flag;++j){
                if(i==j){
                    continue;
                }
                if(d[i][j]||d[j][i]){
                    continue;
                }
                flag=false;
                break;
            }
        }

        if(flag){
            printf("Case %d: Kalimdor is just ahead
",++Case);
        }
        else{
            printf("Case %d: The Burning Shadow consume us all
",++Case);
        }

    }

    return 0;
}
View Code

c2.dfs

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

#define MAXN 2010
#define MAXM 10010

struct Edge{
    int to,next;
}edge[MAXM];
int head[MAXN],tot;

void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}

bool d[MAXN][MAXN];//d[i][j]为true表示i可达j
bool vis[MAXN];

void dfs(int t,int t2){
    d[t][t2]=true;
    vis[t2]=true;

    int v;
    int i;
    for(i=head[t2];i!=-1;i=edge[i].next){
        v=edge[i].to;
        if(!vis[v]){
            dfs(t,v);
        }
    }
    
}

int main(){

    int T;
    int N,M;
    int u,v;
    int i,j;
    bool flag;//
    int Case=0;

    scanf("%d",&T);

    while(T--){
        scanf("%d%d",&N,&M);

        init();
        for(i=0;i<M;++i){
            scanf("%d%d",&u,&v);
            addedge(u,v);
        }

        memset(d,false,sizeof(d));
        for(i=1;i<=N;++i){
            memset(vis,false,sizeof(vis));
            dfs(i,i);
        }

        flag=true;
        for(i=1;i<=N&&flag;++i){
            for(j=1;j<=N&&flag;++j){
                if(i==j){
                    continue;
                }
                if(d[i][j]||d[j][i]){
                    continue;
                }
                flag=false;
                break;
            }
        }

        if(flag){
            printf("Case %d: Kalimdor is just ahead
",++Case);
        }
        else{
            printf("Case %d: The Burning Shadow consume us all
",++Case);
        }

    }

    return 0;
}
View Code

J.Contest Print Server

s.模拟。注意:某个请求没完成的话,要从第一张开始打印(刚开始没细读题,就写成继续打印了)。

ps:when the printed pages counter reached s

这个reached,呵呵,应该是大于吧。

代码写的是当纸张大于等于s时,就更新s并且counter清零的话,wrong了。。

#include<iostream>
#include<stdio.h>
using namespace std;

int main(){

    int T;
    int n,s,x,y,mod;
    int i;
    char team_name[105][25];//名字
    char str[20];
    int p[105];//需要纸张数
    int counter;//当前已输出记数

    scanf("%d",&T);

    while(T--){

        scanf("%d%d%d%d%d",&n,&s,&x,&y,&mod);

        for(i=0;i<n;++i){
            scanf("%s%s%d%s",team_name[i],str,&p[i],str);
        }

        counter=0;
        for(i=0;i<n;++i){

            if(counter+p[i]<=s){//可以打印完
                printf("%d pages for %s
",p[i],team_name[i]);
                counter+=p[i];
            }
            else{//counter+p[i]>s,不能打印完
                printf("%d pages for %s
",s-counter,team_name[i]);
                s=(s*x+y)%mod;
                counter=0;

                while(p[i]>s){//不能打印完
                    printf("%d pages for %s
",s,team_name[i]);
                    s=(s*x+y)%mod;
                    counter=0;
                }
                
                printf("%d pages for %s
",p[i],team_name[i]);
                counter+=p[i];
                
            }

        }

        printf("
");

    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/gongpixin/p/5327863.html