GDOI模拟赛Round 1
GDOI模拟赛Round 1
数据结构
题目描述:给出一个长度为\(n\)的序列,支持两种操作:
1、对某段区间都加上一个数
2、给出\(p、k\),求下面表示式对\((10^9+7)\)取模
\]
solution
维护某段区间 第一个数乘1+ 第二个数乘2+ …… +第\(n\)个数乘\(n\)是线段树的经典题型,把问题拆成两部分\((p-k, i),(i, p+k)\),注意一下边界就可以了。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
const int maxn=int(1e5)+100;
const int mod=int(1e9)+7;
struct data
{
int x, y;
LL value;
void init(int _x, int _y, LL _value)
{
x=_x;
y=_y;
value=_value;
}
};
int n, m;
LL tree[maxn*4][3];
LL add[maxn*4];
data dat;
void down(int cur, int L, int R)
{
int mid=(L+R)>>1;
tree[cur<<1][2]=(tree[cur<<1][2]+add[cur]*(mid-L+1)%mod)%mod;
tree[cur<<1 | 1][2]=(tree[cur<<1 | 1][2]+add[cur]*(R-mid)%mod)%mod;
LL tmp=add[cur]*(((LL(1+mid-L+1))*(mid-L+1)/2)%mod)%mod;
tree[cur<<1][0]=(tree[cur<<1][0]+tmp)%mod;
tree[cur<<1][1]=(tree[cur<<1][1]+tmp)%mod;
tmp=add[cur]*(((LL(1+R-mid))*(R-mid)/2)%mod)%mod;
tree[cur<<1 | 1][0]=(tree[cur<<1 | 1][0]+tmp)%mod;
tree[cur<<1 | 1][1]=(tree[cur<<1 | 1][1]+tmp)%mod;
add[cur<<1]=(add[cur<<1]+add[cur])%mod;
add[cur<<1 | 1]=(add[cur<<1 | 1]+add[cur])%mod;
add[cur]=0;
}
void update(int cur, int L, int R)
{
if (L>dat.y || R<dat.x) return;
if (dat.x<=L && R<=dat.y)
{
tree[cur][2]=(tree[cur][2]+dat.value*(R-L+1)%mod)%mod;
LL tmp=dat.value*(((LL(1+R-L+1))*(R-L+1)/2)%mod)%mod;
tree[cur][0]=(tree[cur][0]+tmp)%mod;
tree[cur][1]=(tree[cur][1]+tmp)%mod;
add[cur]=(add[cur]+dat.value)%mod;
return;
}
int mid=(L+R)>>1;
if (add[cur]!=0) down(cur, L, R);
update(cur<<1, L, mid);
update(cur<<1 | 1, mid+1, R);
tree[cur][2]=(tree[cur<<1][2]+tree[cur<<1 | 1][2])%mod;
tree[cur][0]=(tree[cur<<1][0]+tree[cur<<1 | 1][0]+tree[cur<<1 | 1][2]*(mid-L+1)%mod)%mod;
tree[cur][1]=(tree[cur<<1 | 1][1]+tree[cur<<1][1]+tree[cur<<1][2]*(R-mid)%mod)%mod;
}
LL ask(int cur, int L, int R)
{
if (L>dat.y || R<dat.x) return 0;
if (dat.x<=L && R<=dat.y)
{
if (dat.value==2) return tree[cur][2];
if (dat.value==0) return ((tree[cur][0]+tree[cur][2]*(L-dat.x)%mod)%mod);
return ((tree[cur][1]+tree[cur][2]*(dat.y-R)%mod)%mod);
}
int mid=(L+R)>>1;
if (add[cur]!=0) down(cur, L, R);
return ((ask(cur<<1, L, mid)+ask(cur<<1 | 1, mid+1, R))%mod);
}
void init()
{
scanf("%d%d", &n, &m);
for (int i=1; i<=n; ++i)
{
int value;
scanf("%d", &value);
dat.init(i, i, value);
update(1, 1, n);
}
}
void solve()
{
for (int i=1; i<=m; ++i)
{
int nid, p, k;
scanf("%d", &nid);
if (nid & 1)
{
scanf("%d%d", &p, &k);
if (k==0)
{
printf("0\n");
continue;
}
dat.init(max(p-k+1, 1), p, 0);
LL ans=ask(1, 1, n);
dat.init(p, min(p+k-1, n), 1);
ans=(ans+ask(1, 1, n)+mod)%mod;
if (p-k+1<=0)
{
dat.init(1, p, 2);
ans=(ans+ask(1, 1, n)*(k-p)%mod+mod)%mod;
}
if (p+k-1>n)
{
dat.init(p, n, 2);
ans=(ans+ask(1, 1, n)*(p+k-1-n)%mod+mod)%mod;
}
dat.init(p, p, 2);
ans=(ans-ask(1, 1, n)*k%mod+mod)%mod;
while (ans<0) ans+=mod;
printf("%I64d\n", ans);
}
else
{
scanf("%d%d%I64d", &dat.x, &dat.y, &dat.value);
update(1, 1, n);
}
}
}
int main()
{
freopen("gdds.in", "r", stdin);
freopen("gdds.out", "w", stdout);
init();
solve();
return 0;
}
树之谜题
原题TopCoder
题目描述:有一棵\(n\)个结点的树,根的位置放了一个红色标记,其它一些结点放了黑色标记。每当一个有标记的结点,当它相邻某个结点没有标记,则可以把当前标记移到那个没有标记的结点,问红色标记能到那些点。
solution
我从这题中领悟到了爆搜的魅力。首先把这棵树看成是无根树,预处理出当\(i\)的父亲为\(j\)时,\(i\)的子树的结点数。然后再看回是有根树,求出每个结点的子树的黑色结点数。爆搜的时候还是把它看做无根树。
然后以\([prev][cur][sum]\)为状态,表示当前红色标记在\(cur\),且\(cur\)的父亲是\(prev\),\(cur\)的子树中黑色结点数为\(sum\).
如果红色部分的黑色结点数\((total-sum)\)小于红色部分的结点数,那么搜索\([cur][prev][total-sum]\)
然后枚举\(cur\)的儿子\(i\),枚举分配\(j\)个黑色结点给它,能这样分配的条件是1、\(j\)小于\(i\)子树的结点数,2、\((sum-j)\)个黑色结点能塞进\(cur\)的其它儿子那里,即\((sum-j)\)小于\(cur\)其它儿子的子树结点数总和,满足上述条件就可以搜索\([cur][i][j]\).
因为有用状态不多,所以不会爆空间
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <set>
#include <map>
using namespace std;
const int maxn=310;
struct LINK
{
int id, next;
};
struct data
{
int cur, prev, sum;
};
int n;
int now, qsize;
int black[maxn], ans[maxn];
int h[maxn], size[maxn][maxn], sum[maxn];
bool vis[maxn][maxn][maxn];
data q[maxn*maxn*5];
LINK t[maxn*2];
void join(int u, int v)
{
t[now].id=v; t[now].next=h[u]; h[u]=now++;
t[now].id=u; t[now].next=h[v]; h[v]=now++;
}
void init()
{
scanf("%d", &n);
for (int i=0; i<n; ++i) h[i]=-1;
for (int i=0; i<n; ++i)
{
int v;
scanf("%d", &v);
if (i==0) continue;
join(i, v);
}
for (int i=0; i<n; ++i)
{
int flag;
scanf("%d", &flag);
if (i==0) continue;
black[i]=flag;
}
}
int calc_size(int prev, int cur)
{
if (size[prev][cur]>0) return size[prev][cur];
size[prev][cur]=1;
for (int i=h[cur]; i>-1; i=t[i].next)
if (t[i].id!=prev)
size[prev][cur]+=calc_size(cur, t[i].id);
return size[prev][cur];
}
int calc_s(int prev, int cur)
{
sum[cur]=black[cur];
for (int i=h[cur]; i>-1; i=t[i].next)
if (t[i].id!=prev)
sum[cur]+=calc_s(cur, t[i].id);
return sum[cur];
}
void insert(int prev, int cur, int sum)
{
if (vis[prev][cur][sum]) return;
vis[prev][cur][sum]=true;
q[++qsize].prev=prev;
q[qsize].cur=cur;
q[qsize].sum=sum;
}
void solve()
{
for (int i=0; i<n; ++i)
for (int j=h[i]; j>-1; j=t[j].next)
calc_size(i, t[j].id);
calc_s(-1, 0);
qsize=0;
for (int i=h[0]; i>-1; i=t[i].next)
if (size[0][t[i].id]>sum[t[i].id])
insert(0, t[i].id, sum[t[i].id]);
int total=sum[0];
ans[0]=true;
for (int i=1; i<=qsize; ++i)
{
int prev=q[i].prev;
int cur=q[i].cur;
int s=q[i].sum;
ans[cur]=true;
if (total-s<size[cur][prev])
insert(cur, prev, total-s);
int sn=0;
for (int j=h[cur]; j>-1; j=t[j].next)
if (t[j].id!=prev) sn+=size[cur][t[j].id];
for (int j=h[cur]; j>-1; j=t[j].next)
if (t[j].id!=prev)
{
for (int k=0; k<=s && k<size[cur][t[j].id]; ++k)
if (sn-size[cur][t[j].id]>=s-k)
insert(cur, t[j].id, k);
}
}
}
int main()
{
freopen("puzzle.in", "r", stdin);
freopen("puzzle.out", "w", stdout);
init();
solve();
for (int i=0; i<n; ++i)
printf("%d ", ans[i]);
return 0;
}
不公平的比赛
题目描述:M和C决定来场摔跤比赛。当然不是他们亲自对战,而是他们各派出\(n\)名勇士进行对战,不妨用\(M_i\),表示M的标号为\(i\)的勇士,同样的\(C_i\)表示C的标号为\(i\)的勇士,其中对勇士的标号从\(0\)开始。
比赛分\(n\)场进行。
第\(i\)场比赛,先是\(M_{i-1}\)和\(C_0\)对战。如果打成平局,那么进行下一局比赛,由\(M_i\)和\(C_1\)对战。如果还是打成平局,那么再进行下一局比赛,由\(M_{i+1}\)和\(C_2\)对战,以此类推,直到决出胜负为止,也就是说,如果前$j (1 \leq j < n) \(局都是平局,则下一局将由\)M_{ (i+j-1) % n}\(和\)C_j\(对决。如果一直打到\)M_{i-2}\(和\)C_{n-1}\(都没有分出胜负,那么第\)i$场比赛平。
胜负有对战双方的力量值决定。
求出M和C各赢了多少场,以及一共进行了多少局比赛。
solution
其实就是以\(M\)为被匹配串,\(C\)为标准串,求以第\(i\)位为开头的后缀的最长公共前缀。用扩展KMP可以轻松解决。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <set>
#include <map>
using namespace std;
const int maxn=int(1e5)+100;
typedef long long LL;
int n;
int st[maxn*2];
int str[maxn];
int ans[maxn], next[maxn];
LL s;
int sm, sc;
void init()
{
scanf("%d", &n);
for (int i=0; i<n; ++i) scanf("%d", &st[i]);
for (int i=0; i<n; ++i) scanf("%d", &str[i]);
for (int i=0; i<n; ++i) st[i+n]=st[i];
}
void solve()
{
next[0]=n;
for (int i=0; i<n; ++i)
if (str[1+i]==str[i]) ++next[1];
else break;
for (int i=2, p=1, maxid=next[1]+1; i<n; ++i)
{
if (next[i-p]+i<maxid) next[i]=next[i-p];
else
{
next[i]=max(maxid-i, 0);
while (i+next[i]<n && str[next[i]]==str[i+next[i]]) ++next[i];
if (i+next[i]>=maxid)
{
maxid=i+next[i];
p=i;
}
}
}
for (int i=0; i<n; ++i)
if (st[i]==str[i]) ++ans[0];
else break;
for (int i=1, p=0, maxid=ans[0]; i<n; ++i)
{
if (next[i-p]+i<maxid) ans[i]=next[i-p];
else
{
ans[i]=max(maxid-i, 0);
while (ans[i]<n && str[ans[i]]==st[i+ans[i]]) ++ans[i];
if (i+ans[i]>=maxid)
{
maxid=i+ans[i];
p=i;
}
}
}
for (int i=0; i<n; ++i)
{
if (ans[i]==n)
{
s+=n;
continue;
}
s+=ans[i]+1;
if (st[i+ans[i]]>str[ans[i]]) ++sm;
else ++sc;
}
printf("%d %d %I64d\n", sm, sc, s);
}
int main()
{
freopen("wrestling.in", "r", stdin);
freopen("wrestling.out", "w", stdout);
init();
solve();
return 0;
}
GDOI模拟赛Round 1的更多相关文章
- 水(NOIP模拟赛Round #10)
题目描述: 小Z有一个长度为的数列.他有次令人窒息的操作,每次操作可以使某个数字或.他当然是希望这些数字的乘积尽量小了.为了简化题目,你只需输出操作完成后的数列即可. ———————————————— ...
- 小红帽的画笔(NOIP模拟赛Round 7)
又到了神奇的模拟赛时间~ 真是丧~ 好吧我们来看看题目 小红帽是Pop star上最著名的人类画家,她可以将任何画出的东西变成真实的物品.赋予她这样神奇能力的正是她手上的画笔. 小红帽每次作画时,都需 ...
- 题(NOIP模拟赛Round #10)
题目描述: 有一张的地图,其中的地方是墙,的地方是路.有两种操作: 给出个地点,询问这个地点中活动空间最大的编号.若询问的位置是墙,则活动空间为:否则活动空间为询问地点通过四联通能到达的点的个数.如果 ...
- 大(NOIP模拟赛Round #10)
题目描述: 小Z有个n个点的高清大图,每个点有且只有一条单向边的出边.现在你可以翻转其中的一些边,使他从任何一个点都不能通过一些道路走回这个点.为了方便,你只需输出方案数对取模即可.当在两个方案中有任 ...
- bananahill(NOIP模拟赛Round 8)
题目描述 香蕉川由座香蕉山组成,第i座山有它的高度.小Z准备从左到右爬这里的恰好座香蕉山,但他不希望山的高度起伏太大,太过颠簸,会让本就体育不好的他过于劳累.所以他定义了爬山的劳累度是所有爬的相邻的两 ...
- 战斗机的祈雨仪式(NOIP模拟赛Round 7)
[问题描述] 炎炎夏日,如果没有一场大雨怎么才能尽兴?秋之国的人民准备了一场祈雨仪式.战斗机由于拥有操纵雷电的能力,所以也加入了其中,为此,她进行了一番准备. 战斗机需要给自己的Spear of Lo ...
- 魔法使的烟花(NOIP模拟赛Round 7)
[问题描述] 魔法森林里有很多蘑菇,魔法使常常采摘它们来制作魔法药水.为了在6月的那个奇妙的晚上用魔法绽放出最绚丽的烟花,魔法使决定对魔法森林进行一番彻底的勘探. 魔法森林分为n个区域,由n-1条长度 ...
- 灰姑娘的水晶鞋(NOIP模拟赛Round 7)
[问题描述] 传说中的水晶鞋有两种颜色:左边的水晶鞋是红色,右边的是蓝色,据说穿上它们会有神奇的力量. 灰姑娘要找到她所有的n双水晶鞋,它们散落在一条数轴的正半轴上,坐标各不相同,每双水晶鞋还有一个权 ...
- YYH的球盒游戏(NOIP模拟赛Round 6)
题目描述 YYH有一些总共有种颜色的球,他有颜色的球个.他同样有个盒子,第个盒子能放个球. 他的目标是把这个球按规则放进个盒子里: 对于一个盒子,对于每种颜色的球至多只能放个. 把颜色为的球放进盒子, ...
随机推荐
- Spring中的DataBinding(二) - Validation
@Controller@RequestMapping(value = "/custom/register")public class RegistrationController ...
- 配置阿里云作为yum 源
第一步:下载aliyum 的yum源配置文件. http://mirrors.aliyun.com/repo/ 第二步:把下载到的repo文件复制到/etc/yum.repo.d/目录下. ----- ...
- python学习day3
目录: 1.集合set 2.计数器 3.有序字典 4.默认字典 5.可命名元组 6.队列 7.深浅拷贝 8.函数 9.lambda表达式 10.内置函数 一.集合set set是一个无序且不重复的元素 ...
- UI组件
1.自定义View 2.布局管理器-----ViewGroup 3.textview及其子类 4.imageview及其子类 5.adapterview及其子类----ViewGroup 6.prog ...
- YY的困境:除了终止私有化 还有更多的担忧
界面 刘莎 已大热一段时间的中概股私有化浪潮随着中国股市的下跌而降温,很多在美上市的中概股不得不因此叫停私有化,欢聚时代(下称YY)首当其冲,成为私有化大军中首个被迫撤退的中资公司. 虽然从表面看,私 ...
- C++模板类中使用静态成员变量(例如Singleton模式)
一个最简单Singleton的例子: ///////// Test.h /////////template <class _T>class CTest{private:_T n;stati ...
- 经典CSS颜色混合模式
转自:http://www.webhek.com/css-blend-mode/ 注意:只有使用最新版的谷歌浏览器.火狐浏览器,才能正确的显示本文中的演示. Photoshop里最没有用处的一种功能— ...
- redhat enterprise 6.3 x86_64 上安装VirtualBox详细教程
这个教程真难找..... 安装第一步遇到的问题就是不能使用yum安装包,这是由于redhat是收费版,所以需要更新yum源列表,具体可以参考 http://www.cnblogs.com/tina-s ...
- 如何不让oracle使用linux的swap分区
经常看到swap分区被使用,被缓存的内容本来是为了增加命中率,结果去不断换入换出,导致本地磁盘IO增加,影响访问速度.所以在内存充足的情况下,如果我们觉得不需要使用swap分区的时候,那就要想办法尽量 ...
- c++崩溃错误2
使用了内联函数: 在头文件中声明和定义内联函数是正确的 但是在头文件中声明内联函数,而在.cpp文件中定义了内联函数会导致崩溃的 .h class stu{ inline void str(): } ...