前言
这次考试有三个人AK,而我却因为被第三题我原来的做法虽然能够oj(难道是oj上的数据水?),被卡成50分,然后就250了。
然后这次考试跟平常理解的dp没有多大的关系。
题目
1.祖先们都在看着你(ancestors.pas/c/cpp)
Time Limit:1s Memory Limit:256MB
【题目描述】
公元前 2000 年。在某块草原中,生活着一群牛,它们都有着自己的图腾和信仰。因此,不同信仰的牛们很自然地分为了各个部族。为了自己所在部族的神灵,牛们经常会发起圣战,为神灵夺取地盘。在很长一段时间内,各个部族的矛盾几乎到达了不可调和的地步!这时,在众牛中,出现了一位豪杰,他在短时间内将各部族拉入麾下,建立起了血蹄部族。他就是后来为牛们所传颂的伟人:孛儿只斤•彰异牛,又名蜃牛•血蹄。他给牛们带来了文明。教会了牛们只用两个后蹄走路,空出前蹄来工作,以致于出现了后来谦逊而不失高贵,致力于侍奉自然的牛头人族。而那片草原正是后来的莫高雷。(求不吐槽……)
牛头人们的生活不是和平的,它们侍奉自然,对破坏自然平衡、亵渎大地母亲的行为深恶痛绝,所以,它们有着自己训练有素的军队。孛儿只斤•彰异牛逝世多年后,由于牛头人们的信仰,它们一直相信祖先彰异牛在看着它们(囧),正因为如此,它们在军队训练过程中没有丝毫懈怠。
某天,在彰异牛的注视下,N 个牛头人(依次标号 1 到 N)加入了军队,由某牛头人队长带领。而军队审查了他们的资料后,给出了 M 条指示,第 i 条指示的内容是:第 Ai 号牛头人必须站在第 Bi 号牛头人的左边,指示之间不会出现矛盾。牛头人队长拿到指示后,便开始规划 N 个牛头人的列队方式了。
现在,你需要用计算机的力量秒杀(1s 运行时间搞定)牛头人队长!
【输入格式】
第一行 2 个数字 N,M。
接下来 M 行,每行两个数字 Ai 、 Bi 。
【输出格式】
输出 N 行,即 N 个牛头人的站队序列。如果有多个站队序列满足要求,输出任意一个即可。
【样例输入输出】
ancestors.in
5 4
1 2
2 3
3 4
4 5
ancestors.out
1
2
3
4
5
【样例解释】
1 在 2 前,2 在 3 前,3 在 4 前,4 在 5 前、只有排列 12345 满足要求。
【数据范围】
10%的数据 1≤N≤8 、 1≤M≤28 。
40%的数据 1≤N≤1000 、 1≤M≤105 。
100%的数据 1≤N≤105 、 1≤M≤106 。
【题解】
题目太水,显然的拓扑排序求拓扑序列。
写法很多。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
template< typename Type >inline void Read( Type &In ){
In=0;char ch=getchar();
for( ;ch> '9'||ch< '0';ch=getchar() );
for( ;ch>='0'&&ch<='9';ch=getchar() )In = In*10 + ch-'0';
}
typedef long long LL;
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
const int size = 1000000+10;
int n,m,cnt;
int du[size],head[size],val[size],nxt[size];
bool vis[size];
queue<int> q;
void add_edge(int x,int y) {
nxt[cnt]=head[x];val[cnt]=y;head[x]=cnt++;
}
int main() {
freopen("ancestors.in","r",stdin);
freopen("ancestors.out","w",stdout);
memset(head,-1,sizeof head);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int x,y;Read( x );Read( y );//scanf("%d%d",&x,&y);
add_edge(x,y);du[y]++;
}
for(int i=1;i<=n;i++)
if(!du[i])q.push(i);
while(!q.empty()) {
int now=q.front();q.pop();
if(!vis[now]) {
vis[now]=true;printf("%d\n",now);
}
for(int i=head[now];i!=-1;i=nxt[i]) {
du[val[i]]--;
if(!du[val[i]]) q.push(val[i]);
}
}
return 0;
}
2.愿大地母亲保佑你(bless.pas/c/cpp)
Time Limit:1s Memory Limit:256MB
【题目描述】
牛头人们的生活从来不是和平的。刺背野猪人、鹰身人等种族经常来骚扰他们,还有一个很欠抽的某巨魔及其麾下的军队。
某天,仍然是在彰异牛的注视下,某牛头人临危受命,在大地母亲的保佑下,拿回了一张图纸,图纸上画的是某巨魔的组织关系示意图。这个图中包含的是某巨魔组织的从属关系(直属上级和直属下级),图绘制出来后的形态是 N 个点的树,但是这张图没有告诉牛头人对于一对有从属关系的巨魔,谁是上级谁是下级。巨魔中肯定有个最高指挥官(即没有从属上级的巨魔),但是牛头人现在不好判断哪个巨魔是最高指挥官了。
经过一个牛头人大德鲁伊分析后,得出结论,如果将某个巨魔从这个关系图中去掉后,这个图剩下的连通块中,点数最多那个块在所有去掉的方案中的点数最少,那么这个巨魔可能就是最高指挥官。现在,牛头人需要你找出一个最高指挥官。
【输入格式】
第一行 1 个数字 N。
接下来 N-1 行,每行两个数字 Ai、Bi,表示 Ai号巨魔和 Bi号巨魔之间有从属关系。
【输出格式】
输出一个数字,即巨魔最高指挥官的编号。如果有多个输出编号最小的那个。
【样例输入输出】
bless.in
3
1 2
2 3
bless.out
2
【样例解释】
1 在图中去掉后,剩下的连通块点数最多的为 2 个。2 在图中去掉后,剩下的连通块点
数最多的为 1 个,3 在图中去掉后,剩下的连通块点数最多为 2 个。
【数据范围】
20%的数据 1≤N≤1000 。
100%的数据 1≤N≤106 。
【题解】
首先无向图转有向图;
当一个点被删掉之后,产生的连通块有:
一:此点上方的所有点。
二:以此点的每一个儿子为根的子树。
然后就可以愉快的AC了。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
typedef long long LL;
const int size = 1000000+10;
template< typename Type >inline void Read( Type &In ){
In=0;char ch=getchar();
for( ;ch> '9'||ch< '0';ch=getchar() );
for( ;ch>='0'&&ch<='9';ch=getchar() )In = In*10 + ch-'0';
}
int n,cnt;
int nxt[size<<1],head[size<<1],val[size<<1],f[size<<1];
int maxn=0x3f3f3f3f,ans;
void add_edge(int x,int y) {
nxt[cnt]=head[x];val[cnt]=y;head[x]=cnt++;
}
namespace my_dfs {
void dfs(int now,int pre) {
f[now]=1;int Maxn=-1;
for(int i=head[now];i!=-1;i=nxt[i])
if(val[i]!=pre){
dfs(val[i],now);f[now]+=f[val[i]];
Maxn=Max(Maxn,f[val[i]]);
}
Maxn=Max(Maxn,n-f[now]);
if(maxn>Maxn){
maxn=Maxn;
ans=now;
}
}
}
int main() {
freopen("bless.in","r",stdin);
freopen("bless.out","w",stdout);
memset(head,-1,sizeof head);
Read(n);
for(int i=1;i<n;i++) {
int x,y;Read(x);Read(y);
add_edge(x,y);add_edge(y,x);
}
my_dfs::dfs(1,0);
printf("%d\n",ans);
return 0;
}
3.大地母亲在忽悠着你(cheat.pas/c/cpp)
Time Limit:1s Memory Limit:256MB
【题目描述】
大地母亲在护佑着你……
……
对不住了,我不想写背景了。
……
某牛头人:愿大地母亲宽恕你的罪行……
……
如何得到宽恕?
……
某牛头人:计算 ∑mi=1nmodi 的值。
……
大地母亲你真在忽悠我……
【输入格式】
第一行 2 个数字 M、N。
【输出格式】
输出一个数字,即答案。
【样例输入输出】
cheat.in
5 3
cheat.out
7
【样例解释】
3mod1=0
3mod2=1
3mod3=0
3mod4=3
3mod5=3
答案为 1+3+3=7 。
【数据范围】
20%的数据 1≤N,M≤106 。
100%的数据 1≤N,M≤109 。
【题解】
原题:[CQOI2007]余数求和
他们那些AK的人的做法暴力分块求,而我当时的一个数学方法后面竟然TLE了.
这里给出一个AC的数学方法
找规律可知:
f(i+1)=kmod(i+1)=k−q(i+1)=k−qi−q=f(i)−q
(一个显然的等差数列递推式);
也就是说两个相邻的自然数,若被k除的商相同,则被k取模后的两个数相差-q。
所以,只要找出一个区间[i,j],使得k/i=k/(i+1)=…=k/j,即可用等差数列公式求出
kmodi+kmod(i+1)+...+kmodj
。
这个任务就是:解方程[k/x]=p。
可以轻易得到
px≤k<(p+1)x
,而我们只关注
px≤k
,即
x≤kp
,得出
x=[kp]
。
对于每一个i,令
p=[k/i],q=kmodi,j=min(n,kp)
。
根据等差数列公式得到
kmodi+...+kmodj=q∗(j−i+1)−(j−i+1)∗(j−i)2∗p
。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
LL ans,n,k,p,q;
inline LL read() {
LL in=0;char ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar())
in=in*10+ch-'0';
return in;
}
int main() {
freopen("cheat.in","r",stdin);
freopen("cheat.out","w",stdout);
n=read();k=read();
for(LL i=1; i<=n; i++) {
p=k/i,q=k%i;
LL j=p?k/p:n;
if(j>n)j=n;
ans+=q*(j-i+1)-(j-i+1)*(j-i)/2*p;
i=j;
}
printf("%lld\n",ans);
return 0;
}
总结
排名 名称 bless cheat ancestors 总分
1 Lcy 100 100 100 300
1 Yzy 100 100 100 300
1 yyj 100 100 100 300
%%%%%% mod mod mod