Test 9 for NOIP- Result for Day1

版权声明:本文为博主原创文章,未经博主允许你想转还是转吧总之我这点东西还有版权这说法吗呵呵呵呵…… https://blog.csdn.net/AVALON_X/article/details/77621501

头大

这个暑假完就要去搞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个再说。

没有更多推荐了,返回首页