2015 ACM/ICPC Asia Regional Shenyang Online

27 篇文章 0 订阅

1002 Best Solver  即HDU 5451 

咋一看,跟人家的那道长沙邀请赛的题目很像。只不过这里的幂是2^x+1

如果能求出幂就好了,x那么大,势必要用快速幂。

事实上可以先求出n=(2^x)%((m-1)*(m+1))+1;

然后用矩阵快速幂。

两个快速幂的结合啊。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<fstream>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<queue>
#define LL long long
using namespace std;
typedef vector<LL>vec;
typedef vector<vec>mat;
LL m,n,x;
int Case=1;
mat mul(mat &a,mat &b)
{
    mat c(a.size(),vec(b[0].size()));
    for(int i=0;i<a.size();i++)
        for(int k=0;k<b.size();k++)
            for(int j=0;j<b[0].size();j++)
            c[i][j]=(c[i][j]+a[i][k]*b[k][j])%m;
    return c;
}
mat pow(mat a,LL n)
{
    mat b(a.size(),vec(a[0].size()));
    for(int i=0;i<a.size();i++) b[i][i]=1;
    while(n)
    {
        if(n&1) b=mul(b,a);
        a=mul(a,a);
        n>>=1;
    }
    return b;
}
LL quick_mod(LL a,LL n,LL m)
{
    LL r=1;
    while(n)
    {
        if(n&1) r=(r*a)%m;
        n>>=1;
        a=(a*a)%m;
    }
    return r;
}
void solve()
{
    n=quick_mod(2,x,(m-1)*(m+1))+1;
    mat a(2,vec(2));
    a[0][0]=5;a[0][1]=24;
    a[1][0]=1;a[1][1]=5;
    a=pow(a,n);
    LL ans=(a[0][0]*2-1+m)%m;
    printf("Case #%d: %I64d\n",Case++,ans);
}
int main()
{
   // freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%I64d%I64d",&x,&m);
        solve();
    }
    return 0;
}


1003 即 HDU 5452 Minimum Cut

出题人也是6,一道傻逼题被描述得很难的样子,不光卡题意,还卡常,我大概估计了一下读数据就花了1000+ms,说好的标程三倍时限呢。

题意:给出n个点,然后给出一颗这n个点的生成树的边,最后再给出一些边,这样就成了一个图了,现在,要做的事情就是删掉最少的边,使得图不连通,然后必须删一条生成树的边,生成树的边也只能删一条。

首先,要明确的是,我们要使得图不连通,使得一个点孤立就可以了,那么,而且这个点必然不能是生成树上的非叶子节点。

所以:第一步,找出生成树上的所有叶子节点。

第二步,统计每个顶点连出去的边(非生成树的边)

那么答案就是叶子节点中连边数的最小值

最后答案还要加1,就是因为要删掉一条生成树的边嘛。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<fstream>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<queue>
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
/*
正反循环的宏定义
*/
#define ffr(i,x,y) for(int i=(x),_en=(y);i<=_en;i++)
#define rff(i,x,y) for(int i=(x),_en=(y);i>=_en;i--)
#define clr(f,z) memset(f,z,sizeof(f))
using namespace std;
const int maxn=20005,inf=1<<29;
int dir[][2]={ {0,1},{-1,0},{0,-1},{1,0},{-1,1},{-1,-1},{1,-1},{1,1}};//常用方向数组
int n,m,t;//常用全局变量
int in[maxn],a[maxn],b[maxn];
struct node
{
    int x,y;
};//常用结构体
vector<int>G[maxn];//常用邻接表
/*
函数重载,可根据参数类型,自动选择输入
*/
bool sf(int &x) { return scanf("%d",&x)==1;}
bool sf(char *x){return scanf("%s",x)==1;}
bool sf(double &x){return scanf("%lf",&x)==1;}
bool sf(LL &x) { return scanf("%I64d",&x)==1;}
void pf(int x,int op) {
    op?printf("%d\n",x):printf("%d ",x);//op==0打印数字加空格,op==1打印数字加换行
}
void pf(LL x,int op) {
    op?printf("%I64d\n",x):printf("%I64d ",x);//op==0打印数字加空格,op==1打印数字加换行
}
int get_rand(int n)
{
    return (int)((double)rand() / RAND_MAX * n) ;
}
int main()
{
     //freopen("in.txt","r",stdin);
     //freopen("out.txt","w",stdout);
     //srand(time(NULL));
    sf(t);
    int Case=1;
    while(t--)
    {
        sf(n);sf(m);
        //ffr(i,0,n) G[i].clear();
        clr(a,0);clr(b,0);
        ffr(i,1,m)
        {
            int x,y;
            sf(x);sf(y);
            if(i>=n) b[x]++,b[y]++;//统计出生成树中顶点连出去的边数
            else a[x]++,a[y]++;//统计每个顶点连出去的边数
        }
        int ans=1<<30;
        ffr(i,1,n)
        {
            if(a[i]>=2) continue;//非叶子节点
            ans=min(ans,b[i]);
        }
        printf("Case #%d: %d\n",Case++,ans+1);
    }
    return 0;
}


1012 Largest Point 即 HDU 5461 Largest Point

给定n个数 t1,t2,...tn

给定a,b;求a*ti*ti+b*tj的最大值。

拿到这个题我就知道是个傻逼题,我了大去,然而,我一开始傻逼了,想对a,b是否大于0分类讨论。

妈蛋,分类讨论了快一个小时,越讨论,感觉越复杂。就放弃了这种想法,开始想新的算法。

后来,想到了正确做法。

令A[i]=a*ti*ti和B[j]=b*tj;

以o(n)的复杂度算出A,B数组。

然后对A,B数组分别排序。分别取出A,B数组最大的两个数就行了。注意A,B数组不能同时取同一个数。。。

下面是AC代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<fstream>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<queue>
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
/*
正反循环的宏定义
*/
#define ffr(i,x,y) for(int i=(x),_en=(y);i<=_en;i++)
#define rff(i,x,y) for(int i=(x),_en=(y);i>=_en;i--)
#define clr(f,z) memset(f,z,sizeof(f))
using namespace std;
const int maxn=1000005,inf=1<<29;
int dir[][2]={ {0,1},{-1,0},{0,-1},{1,0},{-1,1},{-1,-1},{1,-1},{1,1}};//常用方向数组
vector<int>G[maxn];//常用邻接表
/*
函数重载,可根据参数类型,自动选择输入
*/
bool sf(int &x) { return scanf("%d",&x)==1;}
bool sf(char *x){return scanf("%s",x)==1;}
bool sf(double &x){return scanf("%lf",&x)==1;}
bool sf(LL &x) { return scanf("%I64d",&x)==1;}
void pf(int x,int op) {
    op?printf("%d\n",x):printf("%d ",x);//op==0打印数字加空格,op==1打印数字加换行
}
void pf(LL x,int op) {
    op?printf("%I64d\n",x):printf("%I64d ",x);//op==0打印数字加空格,op==1打印数字加换行
}
int get_rand(int n)
{
    return (int)((double)rand() / RAND_MAX * n) ;
}
LL a,b,num[maxn];
LL get(LL x,LL y)
{
    return a*x*x+b*y;
}
struct node{
    LL x;
    int id;
} A[maxn],B[maxn];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
int main()
{
     //freopen("in.txt","r",stdin);
     //freopen("out.txt","w",stdout);
     //srand(time(NULL));
     int n,t,Case=1;
    sf(t);
    while(t--)
    {
        sf(n);sf(a);sf(b);
        //pf(n,0),pf(a,1),pf(b,1);
        ffr(i,0,n-1)
        {
            sf(num[i]);
            A[i].x=a*num[i]*num[i];
            A[i].id=i;
            B[i].x=b*num[i];
            B[i].id=i;
        }
        sort(A,A+n,cmp);sort(B,B+n,cmp);
        LL ans;
        if(A[n-1].id!=B[n-1].id) ans=A[n-1].x+B[n-1].x;
        else
        {
            ans=max(A[n-1].x+B[n-2].x,A[n-2].x+B[n-1].x);
        }
        printf("Case #%d: %I64d\n",Case++,ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值