20181026(整除分块+概率递推+树上操作+(神秘中位数+rmq))

快速荷叶叶变换
(fht.cpp/c/pas)
【问题描述】
荷叶叶是一位伟大的数♂学家。
荷叶叶发明了一个函数,并称之为快速荷叶叶变换(Fast H10Transfrom)
FHT(N, M) =在这里插入图片描述
但荷叶叶比较懒,对于函数的计算,荷叶叶把这个任务交给了你。
由于答案可能会很大,请输出答案对1000000007取模的值。
【输入格式】
一行,包含两个整数N,M。
【输出格式】
1个整数,FHT(N,M) mod 1000000007的值。
【输入输出样例】
in
3 4
out
1
【数据规模与约定】
对于 40%的数据,1 ≤N,M≤1000
对于 60%的数据,1 ≤N,M≤106
对于 100%的数据,1 ≤N,M≤109
全国信息学奥林匹克联赛(NOIP2016)复赛 提高组 day0
第3页共4页
幻象
(phantom.cpp/c/pas)
【问题描述】
phantom是一位爱思考的哲♂学家。
最近phantom得到了森の妖精的真传。在他练功的时候,每秒他的思绪中都有一定的概率浮现出奇♂异的幻象,持续x秒的幻象将产生的幻象值。
phantom练功发自真心,他想知道,在N秒内他期望产生的幻象值是多少。
【输入格式】
第一行包含1 个正整数 N,表示总时间 N秒。
第二行包含 N个用空格隔开的在[0,100]之间的正整数,其中第i个数a[i]表示第i秒浮现幻象的概率为百分之a[i]。
【输出格式】
1个实数,四舍五入后保留一位小数,表示期望幻象值。
【输入输出样例】
in
3
50 50 50
out
2.8
【数据规模与约定】
对于 40%的数据 N ≤ 10
对于 60%的数据 N ≤ 100
对于 100%的数据,N ≤ 106
数据规模较大,请使用效率较高的读入方式。
全国信息学奥林匹克联赛(NOIP2016)复赛 提高组 day0
第4页共4页
树上摩托
(sherco.cpp/c/pas)
【问题描述】
Sherco是一位经验丰富的魔♂法师。
Sherco在第零次圣杯战争中取得了胜利,并取得了王之宝藏——王の树。
他想把这棵树砍去任意条边,拆成若干棵新树,并装饰在他的摩托上,让他的摩托更加酷炫。
但Sherco认为,这样生成的树不具有美感,于是Sherco想让每棵新树的节点数相同。
他想知道有多少种方法分割这棵树。
【输入格式】
第一行一个正整数N,表示这棵树的结点总数。
接下来N-1行,每行两个数字X,Y表示编号为X的结点与编号为Y的结点相连。结点编号的范围为[1,N]。
【输出格式】
一个整数,表示方案数。注意,不砍去任何一条边也算作一种方案。
【输入输出样例】
in
6
1 2
2 3
2 4
4 5
5 6
out
3
【数据规模与约定】
对于40%的数据,N ≤ 15
对于60%的数据,N ≤ 105
对于100%的数据,N ≤ 106
数据规模非常大,请使用高效的读入方式。
2 beautiful
2.1 题目􁧿述
一个长度为n 的序列,对于每个位置i 的数ai 都有一个优美值,其定义是:找到序列中最
长的一段[l, r],满足l i r,且[l, r] 中位数为ai(我们比较序列中两个位置的数的大小时,
以数值为第一关键字,下标为第二关键字比较。这样的话[l, r] 的长度只有可能是奇数),r - l

  • 1 就是i 的优美值。
    接下来有Q 个询问,每个询问[l, r] 表示查询区间[l, r] 内优美值的最大值。
    2.2 输入􁹬􁔿
    第一行输入n 接下来n 个整数,代表ai 接下来Q,代表有Q 个区间接下来Q 行,每行
    两个整数l, r(l r),表示区间的左右端点
    2.3 输出􁹬􁔿
    对于每个区间的询问,输出答案
    2.4 Sample Input
    8
    16 19 7 8 9 11 20 16
    8
    3 8
    1 4
    2 3
    1 1
    5 5
    1 2
    2 8
    7 8
    2.5 Sample Output
    7
    3
    1
    3
    5
    3
    7
    3
    3

T1
题意已经给了
整除分块
移步我的博客
如果你已经会了,那么实在是,太简单了。。。

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
long long n,m;
long long ans1,ans2;
int main()
{
	scanf("%d%d",&n,&m);
	ans1=n*n;
	for (long long r,l=1; l<=n; l=r+1)
	  {
	  	if (n/l!=0) r=min(n,n/(n/l)); else r=n;
	  	ans1=(ans1-(l+r)*(r-l+1)*(n/l)/2)%mod;
	  }
	ans2=m*m;
	for (long long r,l=1; l<=m; l=r+1)
	  {
	  	if (m/l!=0) r=min(m,m/(m/l)); else r=m;
	  	ans2=(ans2-(l+r)*(r-l+1)*(m/l)/2)%mod;
	  }
	printf("%lld",(ans1*ans2)%mod);
}

T2
题意概括:每个时刻有a[i]a[i]的概率练功(?),功力收益是连续时长的平方x2x^2(?),求n时刻的功力收益期望

L[i]L[i]为第ii秒幻象的持续时间的期望.
显然L[i]=(L[i1]+1)a[i]L[i] = (L[i-1] + 1) * a[i]%
f[i]f[i]表示前ii秒的答案
f[i]=f[i1]+((L[i1]+1)2L[i1]2)a[i]f[i] = f[i-1] + ((L[i-1] + 1)^2 – L[i-1]^2) * a[i]%
时间复杂度为O(N)O(N)

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;

double f[1000005],nx[1000005],p[1000005];
int n;

int read(){
    int x=0;
    char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)){
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    return x;
}

int main(){
    n=read();
    int x;
    for(int i=1;i<=n;i++){
        x=read();
        p[i]=x*1.0/100;
    }
    nx[1]=p[1];
    f[1]=p[1];
    for(int i=2;i<=n;i++){
        nx[i]=(nx[i-1]+1)*p[i];
        f[i]=(f[i-1]-nx[i-1]*nx[i-1]+(nx[i-1]+1)*(nx[i-1]+1))*p[i]+(1-p[i])*f[i-1];
    }
    printf("%.1f\n",f[n]);
}

T3
题意概括:把一棵树平均分成相同点数相同的块,求要消去几条边。

据我所知,联通的话,一个节点要联通,只能往上或者往下,如果说一个节点的size满足当前要分成的点数,那么肯定分出来,然后以这个节点为根的子树,就能一起往上联通,作为这个节点的父亲的子树,如果到了根节点刚好分完,那么就刚好。

深搜因为常数太大会爆,O(nn)O(n\sqrt{n})

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int head[maxn],n,m,cnt,yin[maxn],cnty,son[maxn],son1[maxn],ce;
bool p;
struct node
{
    int v,nxt;
}e[maxn<<1];
inline void add(int u,int v)
{
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].v=v;
    head[u]=cnt;
}
inline void build(int u,int fa)
{
 for (int i=head[u];i;i=e[i].nxt)
   {
      int v=e[i].v;
      if (v==fa) continue;
      build(v,u);
      son1[u]+=son1[v];
   }
  son1[u]++;
}
inline int dfs(int u,int fa,int o)
{
    if(son1[u]==o) return o;
    int num=0;
    for (int i=head[u]; i; i=e[i].nxt)
      {
        int v=e[i].v;
        if (v==fa) continue;
        num+=dfs(v,u,o);
        if(son1[u]-num==o) return son1[u];
      }
    return num;
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<n; i++)
      {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
      }
    for (int i=2; i<=sqrt(n); i++)
      if (n%i==0) {if (i*i==n) {cnty++; yin[cnty]=i;} else {cnty++; yin[cnty]=i; cnty++; yin[cnty]=n/i;}}
    build(1,0);
    int ans=0;
    for (int i=1; i<=cnty; i++)
      if (dfs(1,0,yin[i])==n) ans++;
    if (ans==0) ans=-1;
    printf("%d",ans+2);
}

由此,精简,如果子树可以分成每个块,那么所有节点能够刚好除以因数ii的数量等于n/in/i,那么就可以了

# include <bits/stdc++.h>
using namespace std;
const int root=1;
const int MAXN=1e6+10;
int n,head[MAXN],size[MAXN],tot=0;
struct rec{int to,pre;}a[MAXN*2]; 
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void adde(int u,int v)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
void dfs1(int u,int fa)
{
    size[u]=1;
    for (int i=head[u];i;i=a[i].pre){
        int v=a[i].to; if (v==fa) continue;
        dfs1(v,u);
        size[u]+=size[v];
    }
}
bool check(int R)
{
    int cnt=0;
    for (int i=1;i<=n;i++)
     if (size[i]%R==0) cnt++;
    return n/R==cnt; 
}
int main()
{
    n=read();
    int u,v;
    for (int i=1;i<n;i++) {
        u=read();v=read();
        adde(u,v);adde(v,u);
    } 
    dfs1(root,-1);
    int ans=0;
    for (int i=1;i<=sqrt(n);i++) {
        if (n%i) continue;
        if (check(i)) ans++;
        if (i!=n/i&&check(n/i)) ans++;
    }
    printf("%d\n",ans);
    return 0;
}

T4
题意概括:?

暴力求中位数然后rmqrmq最大值
rmqrmq详见我的STST

#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[100100],num[100100],r[1000100];
int f[25][1000100];
int main()
{
//  freopen("beautiful.in","r",stdin);
//  freopen("beautiful.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        memset(num,0,sizeof(num));
        memset(r,0x7f,sizeof(r));
        r[500000]=i;f[0][i]=1;
        int j=i;
        while(j>1)
        {
            j--;
            if(a[j]>a[i]) num[j]=num[j+1]-1;
            else num[j]=num[j+1]+1;
            r[num[j]+500000]=j;
            if(num[j]==0) f[0][i]=max(f[0][i],i-j+1);
        }
        j=i;
        while(j<n)
        {
            j++;
            if(a[j]>=a[i]) num[j]=num[j-1]+1;
            else num[j]=num[j-1]-1;
            f[0][i]=max(f[0][i],j-r[num[j]+500000]+1);
        }
    }
    for(int i=1;i<=20;i++)
        for(int j=1;j<=n;j++)
            f[i][j]=max(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    scanf("%d",&q);
    while(q--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        int k=log(r-l+1)/log(2);
        int ans=max(f[k][l],f[k][r-(1<<k)+1]);
        printf("%d\n",ans);
    }
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章