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