Test 9 for NOIP- Result for Day1

头大

这个暑假完就要去搞NOIP了。。。

暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。


出题人神奇的把NOI的题强行改成了NOIP的题,还强行说T1很简单,更气人的是最后看了题解发现T1还真的那么简单。
随机大法好。orz
T2思路没对,T3第二遍DFS没想对orz。

Day1 (20/300)

T1 举办比赛 (20/100)

题目描述
Mstdream举办了一场抢答比赛!
这场抢答比赛有n支队伍参赛,第i支队伍将会被分配一个随机值si
每一个问题,si值较小的队伍会拥有优先发言权,于是Mstdream想知道所有队伍最小
的si值,以便统计
但是,由于各种各样的原因,一些队伍的si值会变化,Mstdream想让你帮忙,对于每
次修改后求出所有队伍中最小的si值
由于队伍实在太多了!我们将采用数据生成的方式读入数据
我们将给出n,m,x0,x1,a,b,c七个数,对于c++语言,我们将采用如下读入
你可以使用这份代码,也可以不使用

#define uint unsigned int 
uint x0,x1,a,b,c,n,m; 
uint now=1,tot=0; 
uint s[10000010]; 
uint nxt(){ 
  uint t=x0*a+x1*b+c; 
  x0=x1; 
  x1=t; 
  return x0>>2; 
} 
int main(){ 
std::cin>>n>>m>>x0>>x1>>a>>b>>c; 
  for(int i=0;i<n;i++){ 
  s[i]=2147483647; 
  } 
  for(int i=1;i<=m;i++){ 
  int f=nxt(); 
  int g=nxt(); 
  int ps=f%n; 
  s[ps]=g;//此处是修改 
  uint ans=0; 
  //…..此处需要你求出s[0]到s[n-1]的最小值,并且存在ans当中 
  now*=10099; 
  tot+=now*ans; 
  } 
  std::cout<<tot<<std::endl; 
} 

根据相关理论,nxt()函数是随机的。

Input
7个数n,m,x0,x1,a,b,c

Output
一个数,即程序中的tot值

Sample Input
5 5 1 2 3 4 5

Sample Output
3818396440

Sample Input
10000000 10000000 555 888 777 666 12345

Sample Output
4134418848

范围
10% n,m<=1000
40% n,m,<=100000
100% n<=10000000,m<=50000000
保证x0,x1,a,b,c在unsigned int范围内

据出题人说这道题一般别人都会打线段树之类的。。。我会告诉你我用的单调队列吗。。。
20分就这么来的。

最后出题人说事实上按题意来讲最小值最多改变5次左右。。也就是判断一下再搜索一下就行了。。
然后liuyuwei上来又讲了几个随机大法的例子,什么随机n个数求最大最小值一般直接暴力从中间向两侧枚举之类的。

。。头一次发现随机大法还可以这么玩

STD.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define uint unsigned int 

uint x0,x1,a,b,c,n,m,head=0,tail=0; 
uint now=1,tot=0; 
uint s[10000010]; 

inline unsigned int read()
{
    unsigned int i=0;
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
    return i;
}

inline uint nxt(){ 
  uint t=x0*a+x1*b+c; 
  x0=x1; 
  x1=t; 
  return x0>>2; 
} 

uint loc=0;
uint minn=2147483647;

signed main(){ 

  //freopen("contest.in","r",stdin);
  //freopen("contest.out","w",stdout); 
  ios::sync_with_stdio(0);cin.tie(0);
  n=read();m=read();
  x0=read();x1=read();
  a=read();b=read();c=read();

  for(int i=0;i<n;i++){ 
  s[i]=2147483647; 
  } 
  for(int i=1;i<=m;i++){ 
  int f=nxt(); 
  int g=nxt(); 
  int ps=f%n; 
  //此处是修改 

  if(g<minn){loc=ps;minn=g;s[ps]=g;}
  else{
    s[ps]=g;
    if(ps==loc)
    {
        loc = min_element(s,s+n)-s;
        minn = s[loc];
    }
  }

  now*=10099; 
  tot+=now*minn; 
  } 
  cout<<tot<<endl; 
} 

随机大法好,不说别的orz

T2 疯狂的01串

题目描述
szzq有两个01字符串
有一天,他想把两个01字符串变得一样
szzq请来了Mstdream,请他用正好k步,每次改变m个数位将第一个字符串改变为第
二个字符串
改变一个数位的定义是将这一位异或1
Mstdream很快完成了任务
szzq又让Mstdream算出有多少种方案做到这一点,这下Mstdream不会了
请帮助Mstdream完成任务
当两种方案至少有一步中改变的至少有一位不同,我们认为两种方案是不同的

输入格式
第一行n,k,m
第二行 一个长为n的01串
第三行 一个长为n的01串

输出格式
一个数,代表方案数对1000000009取模后的结果

Sample Input
3 2 1
100
001

Sample Output
2
Hint
100->101->001
100->000->001

数据规模
30%数据满足1 ≤ n ≤ 5, 0 ≤ k≤ 2,0 ≤ m ≤ n
100%数据满足 1 ≤ n ≤ 100, 0 ≤ k≤ 100, 0 ≤ m ≤ n

最后30分钟做的,想到了与题解类似的但最后推公式推错了,没考虑用组合数,乱搞搞死了。
还得加强Dp的练习。

STD.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;

int n,k,m,tot=0;
long long f[105][105],c[105][105];
char s[105],t[105];

const int nod=1000000009;

void pre()
{
    for(int i=0;i<=n;i++)c[i][0]=1;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
        c[i][j] = (c[i-1][j] + c[i-1][j-1])%nod;
}

int main()
{
    cin >> n >> k >> m;
    scanf("%s%s",s,t);

    for(int i=0;i<n;i++)
      if(s[i]!=t[i])
        tot++;

    pre();  f[0][tot]=1;    //   j 取 l   个 
    for(int i=0;i<k;i++)    // n-j 取 m-l 个 
        for(int j=0;j<=n;j++)
            for(int l=0;l<=m;l++) //f[i][j]:  i:已经替换的个数;  j:剩余的不同的;
                if(j-l+m-l<=n)    //?????????
                    f[i+1][j-l+m-l]+=(long long)(f[i][j]*c[j][l]%nod*c[n-j][m-l])%nod, 
                    f[i+1][j-l+m-l]%=nod; 
    cout << f[k][0] <<endl;
}

T3 树

题目描述
Mstdream经营着一个国家!
这个国家的道路系统很奇怪,由n个城市和n-1条边构成,1号点为首都,每条边长度
均为1
显然,Mstdream的国家交通很堵塞,特别是首都人民,苦不堪言
终于,Mstdream决定修一条长度为1的道路!这条道路将连通首都以及另一个城市
由于修建道路成本巨大,Mstdream 联系到你,希望你能决定这条道路连接的另外一个
城市,使得首都到所有城市的最短距离之和最小

输入格式
第一行一个整数n,代表节点的数量
后面n-1行,每行两个数u v,表示u到v有一条边
数据保证给出的图是一棵树
输出格式
一个数,代表首都到所有城市的最短距离之和

Sample Input
6
1 2
2 3
3 4
3 5
3 6

Sample Output
8

Hint
选择3号点
d(1, 1) + d(1, 2) + d(1, 3) + d(1, 4) + d(1, 5) + d(1, 6) = 0 + 1 + 1 + 2 + 2 + 2 =
8

数据规模
对于30%数据 n<=200
对于50%数据 n<=2000
对于100%数据 n<=100000
保证答案在32有符号整数范围内

第一遍DFS和大佬想的差不多,但第二遍DFS就瓜起了,推了40分钟公式最后推出个不伦不类的玩意,GG。

感谢大佬zjj(?)的代码,等会重打。

STD.CPP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#define uint unsigned int 
using namespace std;

int getint()
{
    int i=0 ,f=1;char c;
    for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=1e5+5;
int n;
int totdis,ans;
int tot,first[N],next[N<<1],to[N<<1];
int dep[N],fa[N],size[N],son[N];

void add(int x,int y)
{
    next[++tot]=first[x],first[x]=tot,to[tot]=y;
}

void dfs(int u)
{
    size[u]=1;
    for(int e=first[u];e;e=next[e])
    {
        int v=to[e];
        if(v==fa[u])continue;
        fa[v]=u;
        dep[v]=dep[u]+1;//子树的深度 
        totdis+=dep[v];//不加边时的答案 
        dfs(v);
        size[u]+=size[v];//子树拥有的节点数 
    }
}

void solve(int u,int f,int cnt,int delta)//。。觉得cnt和delta的意义不好说。。 应该就是两个辅助计数的数 
{
    if(dep[u]>=2)//只有dep(u)>2时加的边才有实际意义 
    {
        delta+=size[u]-cnt;//size[v]一段对答案的贡献减一,因为距离新更新的点更近。①,详见下面 ②↓↓↓ 
        ans=min(ans,totdis-delta);
    }

    if((dep[u]+1)/2>dep[f])//顶点连接的另外一个点(u)最多影响到他与顶点(1)之间的中点(f)所在的子树 
    {
        f=son[f];
        if(dep[u]>2)cnt-=size[f]-size[son[f]]; //u点前移,f随之前移,cnt更新,结果加上(pre)f所在子树的贡献与当前f所在子树的贡献之差。 
    }

    for(int e=first[u];e;e=next[e])
    {
        int v=to[e];
        if(v==fa[u])continue;

        int tmp1=cnt,tmp2=delta;//记忆化搜索,for循环的第二步是基于之前的cnt与delta而不是DFS后的。

        if(dep[u]>=2)cnt+=size[u]-size[v];//② 记录如果连边连到v所造成的差值,size[u]-size[v]一段的点贡献加一,因为新更新的点在他们之前 

        son[u]=v;
        solve(v,f,cnt,delta);

        if(dep[u]>=2)cnt=tmp1,delta=tmp2;//记忆化搜索,for循环的第二步是基于之前的cnt与delta而不是DFS后的。 
    }
}

int main()
{
    int x,y;
    n=getint();
    for(int i=1;i<n;i++)
    {
        x=getint(),y=getint();
        add(x,y),add(y,x);
    }
    dfs(1);
    ans=totdis;
    solve(1,1,0,0);
    cout<<ans;
    return 0;
}

感想:

学会推公式,推的要准,不然全挂,还有试数据多试几个,试个5个再说。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值