Solved Pro.ID Title Author Source (AC/Submit)Ratio
  5380 Travel with candy SXYZ 2015 Multi-University Training Contest 8 (46/103)44.66%
  5381 The sum of gcd SXYZ 2015 Multi-University Training Contest 8 (208/496)41.94%
  5382 GCD?LCM! SXYZ 2015 Multi-University Training Contest 8 (73/139)52.52%
  5383 Yu-Gi-Oh! SXYZ 2015 Multi-University Training Contest 8 (95/366)25.96%
  5384 Danganronpa SXYZ 2015 Multi-University Training Contest 8 (278/506)54.94%
  5385 The path SXYZ 2015 Multi-University Training Contest 8 (202/546)37.00%
  5386 Cover SXYZ 2015 Multi-University Training Contest 8 (377/1092)34.52%
  5387 Clock SXYZ 2015 Multi-University Training Contest 8 (325/469)69.30%
  5388 Geometer's Sketchpad SXYZ 2015 Multi-University Training Contest 8 (21/58)36.21%
  5389 Zero Escape SXYZ 2015 Multi-University Training Contest 8 (485/953)50.89%
  5390 tree SXYZ 2015 Multi-University Training Contest 8 (28/160)17.50%

HDU 5381

给定一个数组,多次询问,求区间[l,r]内所有子区间的最大公约数之和。

以a[x]为右端点的gcd区间情况最多只有log(a[x])种,因为a[x]的因子只有sqrt(a[x])种,a[x]的因子的因子只有sqrt(sqrt(a[x]))种,....依次类推共有log(a[x])种

如下图x=7的情况

用sum[x]表示以x为右端点的所有gcd区间和

sum[x]=gcd[1]*(r1-l1+1)+gcd[2]*(r2-l2+1)+...+gcd[k]*(rk-lk+1)  (k<=log(a[x]))

遍历数组,用树状数组维护sum,区间更新,动态查询。

用f[l,r]表示区间[l,r]的所有子区间的gcd和,用d[i]表示区间[1,i]同时增加或者减少的量

f[l,r]=d[l]*1+d[l+1]*2+...+d[r]*(r-l+1)

  =sigma(d[i]*(i-l+1)) 

  =sigma(d[i]*i) - (l-1)sigma(d[i])    (l<=i<=r)

用两个数组分别维护d[i] 和 d[i]*i 即可。

计算gcd区间的位置时用了一个nxt数组,表示以x为右端点时区间[nxt[j],j]的gcd值相同

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define low(x) (x&(-x))
#define maxn 10010
#define LL long long
int n,m;
int a[maxn];
LL ans[maxn];
int nxt[maxn];
int val[maxn];
struct node
{
    int l,r,id;
    bool operator < (const node &b)const
    {
        return r<b.r;
    }
}b[maxn];

LL bit[maxn*2][2];
void add(int t,int x,LL val)
{
    while(x>0)
    {
        bit[x][t]+=val;
        x-=low(x);
    }
}
LL getsum(int t,int x)
{
    LL ans=0;
    while(x<maxn)
    {
        ans+=bit[x][t];
        x+=low(x);
    }
    return ans;
}

void update(int l,int r,int val)
{
    add(0,r,(LL)val);
    add(0,l-1,-(LL)val);
    add(1,r,(LL)val*r);
    add(1,l-1,-(LL)val*(l-1));
}

int gcd(int a,int b)
{
    if(a%b==0)return b;
    else return gcd(b,a%b);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&b[i].l,&b[i].r);
            b[i].id=i;
        }
        sort(b+1,b+m+1);
        memset(bit,0,sizeof(bit));
        int k=1;
        nxt[1]=1;
        val[1]=a[1];
        update(1,1,a[1]);
        for(int i=2;i<=n;i++)
        {
            val[i]=a[i];
            nxt[i]=i;
            int j=i;
            while(j>0){
                val[j]=gcd(val[j],a[i]);
                while(nxt[j]>1&&gcd(val[nxt[j]-1],val[j])==val[j])
                {
                    nxt[j]=nxt[nxt[j]-1];
                }
                update(nxt[j],j,val[j]);
               // printf("i=%d %d %d %d\n",i,nxt[j],j,val[j]);
                j=nxt[j]-1;
            }

            while(k<=m&&b[k].r==i)
            {
                ans[b[k].id]=getsum(1,b[k].l)-(b[k].l-1)*getsum(0,b[k].l);
                k++;
            }
        }
       // printf("k=%d\n",k);
        for(int i=1;i<=m;i++)
        {
           // printf("l=%d  r=%d  ans=%d\n",b[i].l,b[i].r,ans[b[i].id]);
           printf("%lld\n",ans[i]);
        }


    }
    return 0;
}
/*

111
7
1 6 2 3 6 30 12
0

2
5
1 2 3 4 5
15
1 1
1 2
1 3
1 4
1 5
2 2
2 3
2 4
2 5
3 3
3 4
3 5
4 4
4 5
5 5


*/
View Code

 

 

 

HDU5383

有n个人,每个人有一个数字s[i], 有两扇门的数字a, b 。数字都可以重复。现要求将这n个人分配到这两个房间,或者只分配到一个房间,问方法数

x的数字根=(x-1)%9+1

dp[i][j]表示前i个人中k个人分配到房间号为j的方法数 (0<=k<=i)

dp[0][0]=0;

最后要分类讨论,先计算所有人的和的数组根sum

sum==root(a+b) 表示两个房间都有人的情况ans=dp[n][a],如果同时sum==a ,则dp[n][a]中包含了只有a的那种情况 ans=dp[n][a]-1

sum==a 表示只有a房间有人的情况

sum==b表示只有b房间有人的情况

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define LL long long
#define maxn 100010
#define mod 258280327
int s[maxn],n,a,b;
int dp[maxn][10];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&a,&b);
        for(int i=1;i<=n;i++)scanf("%d",&s[i]);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            sum=(sum+s[i]-1)%9+1;
            for(int j=0;j<=9;j++)
                dp[i][j]=dp[i-1][j];
            for(int j=0;j<=9;j++)
            {
                int t=(j+s[i]-1)%9+1;
                dp[i][t]=(dp[i][t]+dp[i-1][j])%mod;
            }
        }
        if(sum!=a && sum!=b && sum!=(a+b-1)%9+1)
        {
            puts("0");
            continue;
        }
        int ans=0;
        if(sum==(a+b-1)%9+1)
        {
            ans+=dp[n][a];
            if(sum==a)ans--;
        }
        if(sum==a)ans++;
        if(sum==b)ans++;
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

HDU5384

先给n条询问,再给m个单词(模式串),求模式串出现的次数和

ac自动机模板题

#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
char str[100010][10010];
int num[100010],n,m;
struct Trie
{
    int next[10010*50][28],fail[10010*50],end[10010*50];
    int root,L;
    int newnode()
    {
        for(int i=0; i<26; i++)
        {
            next[L][i]=-1;
        }
        end[L++]=-1;
        return L-1;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char *s)
    {
        int len=strlen(s);
        int now=root;
        for(int i=0; i<len; i++)
        {
            if(next[now][s[i]-'a']==-1)
            {
                next[now][s[i]-'a']=newnode();
            }
            now=next[now][s[i]-'a'];
        }
        if(end[now]==-1)///标记模式串出现的次数
        {
            end[now]=1;
        }
        else
        {
            end[now]++;
        }
    }
    void build()
    {
        queue<int>Q;
        fail[root]=root;
        for(int i=0; i<26; i++)
        {
            if(next[root][i]==-1)
            {
                next[root][i]=root;
            }
            else
            {
                fail[next[root][i]]=root;
                Q.push(next[root][i]);
            }
        }
        while(!Q.empty())
        {
           // printf("**\n");
            int now=Q.front();
            Q.pop();
            for(int i=0; i<26; i++)
            {
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
            }
        }
    }
    void query(char* s)
    {
        memset(num,0,sizeof(num));
        int len=strlen(s);
        int now=root;
        for(int i=0; i<len; i++)
        {
            now=next[now][s[i]-'a'];
            int temp=now;
            while(temp!=root)
            {
                if(end[temp]!=-1)///统计所有模式串出现的次数,num数组在0~m之间定能取到所有end[temp]必不大于m
                {
                    num[end[temp]]+=end[temp];
                }
                temp=fail[temp];
            }
        }
        int ans=0;
        for(int i=0; i<=m; i++)
        {
            if(num[i]>0)
                ans+=num[i];
        }
        printf("%d\n",ans);
    }
} ac;
char s[10005];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)
        {
            scanf("%s",str[i]);
        }
        ac.init();
        for(int i=0; i<m; i++)
        {
            scanf("%s",s);
            ac.insert(s);
        }
        ac.build();
        //printf("**\n");
        for(int i=0; i<n; i++)
        {
            ac.query(str[i]);
        }
    }
    return 0;
}
View Code

 

HDU5386

给你一个n*n原始矩阵和结果矩阵,有以下两种操作,给你m个操作,要求将操作排序,使得原始矩阵转为结果矩阵

L x y: for(int i=1;i<=n;i++)color[i][x]=y;
H x y:for(int i=1;i<=n;i++)color[x][i]=y;

结果矩阵肯定有一行或者一列数字是相同的,只用从结果矩阵出发,每次将数字相同的一行或者一列查询对应的操作,然后标记为-1(-1表示任何颜色都可以),原始矩阵并没有什么卵用。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int s[110][110];
struct node
{
    char op[2];
    int x,y;
}a[550];
int v[550];
int ans[550];
int ok(int k)
{
    int x=a[k].x;
    int y=a[k].y;
    if(a[k].op[0]=='L')
    {
        for(int i=1;i<=n;i++)
        {
            if(s[i][x]!=-1&&s[i][x]!=y)return 0;
        }
        for(int i=1;i<=n;i++)s[i][x]=-1;
    }
    else{
        for(int i=1;i<=n;i++)
        {
            if(s[x][i]!=-1&&s[x][i]!=y)return 0;
        }
        for(int i=1;i<=n;i++)s[x][i]=-1;
    }
    return 1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
       scanf("%d%d",&n,&m);
       for(int i=1;i<=n;i++)
       {
           for(int j=1;j<=n;j++)
           {
               scanf("%d",&s[i][j]);
           }
       }
       for(int i=1;i<=n;i++)
       {
           for(int j=1;j<=n;j++)
           {
               scanf("%d",&s[i][j]);
           }
       }
        for(int i=1;i<=m;i++)
        {
            scanf("%s%d%d",a[i].op,&a[i].x,&a[i].y);
        }
        memset(v,0,sizeof(v));
        int k=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(!v[j]&&ok(j))
                {
                    v[j]=1;
                    ans[k++]=j;
                    break;
                }
            }
        }

        for(int i=m;i>1;i--)printf("%d ",ans[i]);
        printf("%d\n",ans[1]);
    }
    return 0;
}
View Code

 

HDU5387

给你一个时间 计算时针和分针的夹角,时针和秒针的夹角,分针和秒针的夹角

全部转为秒再通分即可

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int fen=12*3600;
int hh,mm,ss;
int a[4],b[4];
int run(int a,int b)
{
    int ans=abs(a-b);
    ans%=fen;
    if(ans>fen/2)ans=fen-ans;
    return ans;
}
int gcd(int a,int b)
{
    if(a%b==0)return b;
    return gcd(b,a%b);
}
void print(int x)
{
    x*=360;
    int g=gcd(x,fen);
    int t1=x/g;
    int t2=fen/g;
    if(t2==1)printf("%d",t1);
    else printf("%d/%d",t1,t2);
}
int main()
{
    int T;
    scanf("%d",&T);
    char str[100];
    while(T--)
    {
        scanf("%s",str);
        sscanf(str,"%d:%d:%d",&hh,&mm,&ss);
        if(hh>=12)hh-=12;
        a[1]=hh*3600+60*mm+ss;
        a[2]=12*(60*mm+ss);
        a[3]=12*60*ss;
        b[1]=run(a[1],a[2]);
        b[2]=run(a[1],a[3]);
        b[3]=run(a[2],a[3]);
        for(int i=1;i<=3;i++)
        {
            print(b[i]);
            printf(" ");
        }
        puts("");
    }
    return 0;
}
View Code

 

posted on 2015-08-16 09:38  kylehz  阅读(229)  评论(0编辑  收藏  举报