2015 Multi-University Training Contest 1

官方题解:http://blog.sina.com.cn/s/blog_15139f1a10102vnx5.html


1001

HDU 5288:http://acm.hdu.edu.cn/showproblem.php?pid=5288

题意:给出一个数组\(a[n]\),定义函数\(f(l,r)\)表示在区间\([l,r]\)内没有\(a_i\)的因子的\(i\)的数量,因子的下标不能为\(i\),求

$$\sum_{i=1}^{n}\sum_{j=i}^{n}f(i,j)\mod(10^9+1)$$


题目可以转换成每个\(a_i\)可以贡献的区间个数为\(K_i\),求出所有\(K_i\)之和

要求\(K_i\)就是找左右离\(a_i\)最近的2个因子,求出左右不包含因子的区间长度\(l,r\)

$$K_i=(l+1)\times (r+1)$$

$$ans=\sum_{i=1}^{n}K_i$$


要求出最近的因子可以预处理10000内所有数的因数,在输入的时候记录每个数出现的位置,因为记录是有序的,所以可以找出离\(a_i\)最近的两个因子


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL __int64
#define N 100005
#define M 10005
#define INF 1<<30
#define MOD 1000000007

int n;
int a[N];
vector<int> fac[M];
vector<int> sit[M];

void init(){
    for(int i=1;i<M;++i){
        for(int j=i;j<M;j+=i){
            fac[j].push_back(i);
        }
    }
}

int main(){
    init();
	while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=10000;i++)
            sit[i].clear();
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            sit[a[i]].push_back(i);
        }
        LL ans=0;
        LL ll,rr;
        int flag;
        for(int i=1;i<=n;++i){
            ll=0,rr=n+1;
            for(int j=0;j<fac[a[i]].size();++j){
                flag=1;
                for(int k=0;k<sit[fac[a[i]][j]].size();++k){
                    if(sit[fac[a[i]][j]][k]<i){
                        ll=MAX(ll,sit[fac[a[i]][j]][k]);
                    }
                    if(sit[fac[a[i]][j]][k]==i)continue;
                    if(sit[fac[a[i]][j]][k]>i){
                        rr=MIN(rr,sit[fac[a[i]][j]][k]);
                        break;
                    }
                }
            }
            ll=i-ll-1;
            rr=rr-i-1;
            ans=(ans+(ll+1)*(rr+1))%MOD;
        }
        printf("%I64d\n",(ans)%MOD);
	}

	return 0;
}


1002

HDU 5289:http://acm.hdu.edu.cn/showproblem.php?pid=5289


题意:求区间内的数两两之间差小于K的区间个数


RMQ+二分


先预处理出区间最大值和最小值

对于每个\(a_i\)可以用二分找出最大的r值满足区间[i,r]的最大值-最小值<K



#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL __int64
#define INF 1<<30

const int MAXN = 100010;
int dp1[MAXN][20];
int dp2[MAXN][20];
int mm[MAXN];

//初始化RMQ, b数组下标从1开始
void initRMQ(int n,int b[]) {
    mm[0] = -1;
    for(int i = 1; i <= n; i++) {
        mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
        dp1[i][0] = b[i];
        dp2[i][0] = b[i];
    }
    for(int j = 1; j <= mm[n]; j++)
        for(int i = 1; i + (1<<j) -1 <= n; i++){
            dp1[i][j] = max(dp1[i][j-1],dp1[i+(1<<(j-1))][j-1]);
            dp2[i][j] = min(dp2[i][j-1],dp2[i+(1<<(j-1))][j-1]);
        }
}
//查询最大值
int rmq1(int x,int y) {
    int k = mm[y-x+1];
    return max(dp1[x][k],dp1[y-(1<<k)+1][k]);
}

//查询最小值
int rmq2(int x,int y) {
    int k = mm[y-x+1];
    return min(dp2[x][k],dp2[y-(1<<k)+1][k]);
}

int n,k;
int a[MAXN];

bool judge(int x,int y){
    if(rmq1(x,y)-rmq2(x,y)<k)
        return true;
    return false;
}

int main() {
    int T;
    scanf("%d",&T);
    int l,r,m;
    LL ans;
    while(T--){
        scanf("%d%d",&n,&k);
        ans=0;
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        initRMQ(n,a);
        for(int i=1;i<=n;++i){
            l=i,r=n;
            int e=i;
            while(l<=r){
                m=(l+r)>>1;
                if(judge(i,m)){
                    e=m;
                    l=m+1;
                }else{
                    r=m-1;
                }
            }
            ans+=(e-i+1);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


1012

题意:给出n个不想交的圆,两人轮流删圆,每次可以删去一个圆以及它包含的所有圆,不能删时为输

树上SG

如果做出圆的包含关系树,就是一个明显的树上SG。
对于树上SG附上一篇相关文章: 树上删边游戏

这里直接说结论

树的删边游戏
从某一棵树上删除一条边,同时删去所有在删除边后不再与根相连的部分。双方轮流进行,无法再进行删除者判定为失败。

定理:
叶子节点的 SG 值为 0;

中间节点的 SG 值为它的所有子节点的 SG 值加 1 后的异或和。


对于建立圆的包含关系树,可以按半径从小到大排序,对于第一个大于并包含它的圆建边,然后break;

当然这是暴力的方法。交C++会TLE,交G++才会AC


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL __int64
#define N 100005
#define INF 1<<30

struct R{
    int x,y,r;
}o[N];

bool cmp(R a,R b){
    return a.r<b.r;
}

//判断包含关系,i被j包含
int judge(int i,int j){
    if((o[i].x-o[j].x)*(o[i].x-o[j].x)+(o[i].y-o[j].y)*(o[i].y-o[j].y)<(o[i].r-o[j].r)*(o[i].r-o[j].r))
        return 1;
    return 0;
}

struct node{
    int to,next;
}edge[N];
int head[N],k;

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

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

int dfs(int u){
    int ret=0;
    for(int i=head[u];i;i=edge[i].next){
        ret^=(dfs(edge[i].to)+1);
    }
    return ret;
}

int main(){

	int T;
	int n;
	scanf("%d",&T);
	int ans,flag;
	while(T--){
        init();
        ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d%d%d",&o[i].x,&o[i].y,&o[i].r);
        }

        sort(o+1,o+n+1,cmp);
        for(int i=1;i<=n-1;++i){
            flag=0;
            for(int j=i+1;j<=n;++j){
                if(judge(i,j)){
                    add(j,i);
                    flag=1;
                    break;
                }
            }
            if(!flag){
                add(0,i);
            }
        }
        add(0,n);
        ans=dfs(0);
        if(ans)printf("Alice\n");
        else   printf("Bob\n");
	}

	return 0;
}





(待续。。。)



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值