项目冲突管理之若有所思

最近参与一电商项目,遇到一项目冲突的案例,感慨颇多,故借此谈一下项目的冲突管理。

部门主管A:为人谦和,有威望

项目经理B:为人直率,说话较直,脾气火爆

项目成员C:脾气古怪,不合群,但心思缜密

项目还在需求分析阶段,各需求还未明确。

A信任C的心思缜密,希望其规划系统流程,C不负众望很快拿出了自己的方案,并以邮件方式发布给各项目成员,希望能集思广益。大家也积极响应,分别就方案提出了自己的想法,B却提出了不同的看法,自己设计了一套流程似乎与C的方案大相径庭(凭我对B的了解,其应该并无否定C的意思,只是表达的太直),为此C大为光火,认为A授权其制定方案就应该采纳其方案。BC之间发生了冲突,项目组人员间的关系发生微妙变化。A出来圆场认为项目处于需求初期,谁的方案都有可能被采纳。A不否认C的方案,只是觉得太过复杂,不太适合初期的开发,需要精简。同时强调B为项目经理,应该全权负责项目调度。过后C离职,由此冲突解决失败。

 

以下内容转至:http://www.mypm.net/blog/user1/channelpm/archives/2007/16045.html

冲突是项目中不可避免的现象,过多的冲突会破坏项目组织的功能,过少的冲突会使项目组织僵化,对冲突实施科学、有效的管理是项目综合管理的一项重要内容。项目沟通管理包括为了确保项目信息及时、适当地产生、收集、传播、保存和处置所必须的过程,项目沟通管理为项目成功所必须的因素——人、想法和信息之间提供了一个关键连接,涉及项目的任何人都应该准备以项目“语言”发送和接收信息,而且必须理解他们以个人身份参与的沟通怎样影响整个项目。通过本文,充分认识项目冲突的来源和种类;掌握项目沟通的模式、沟通的渠道,认识项目有效沟通的障碍,掌握项目沟通管理的内容和方法。我们以下讨论问题时是以项目执行过程中冲突管理,对于在普通管理中的冲突管理将不是本文要讨论的对象。

一、项目中冲突的起因和对项目的影响

()项目中冲突的起因

l冲突及项目冲突的定义

冲突是双方感知到矛盾与对立,是一方感觉到另一方对自己关心的事情产生或将要产生消极影响,因而与另一方产生互动的过程。

项目冲突是组织冲突的一种特定表现形态,是项目内部或外部某些关系难以协调而导致的矛盾激化和行为对抗。

l冲突的起因及过程

n冲突的起因

冲突不会在真空中形成,它的出现总是有理由的。如何进行冲突管理在很大程度上取决于对冲突产生原因的判断,项目中冲突产生原因主要有:

1  沟通与知觉差异。沟通不畅容易造成双方的误解,引发冲突。另外,人们看待事物存在“知觉差异”,既根据主观的心智体验来解释事物,而不是根据客观存在的事实来看待它,比如人们对“半杯水”的不同态度,并由此激发冲突。

2  角色混淆。项目中的每一个成员都被赋与特定的角色,并给予一定的期望。但项目中常存在“在其位不谋其政,不在其位却越俎代庖”等角色混淆,定位错误的情况。

3  项目中资源分配及利益格局的变化。如目前国资委在中央项目中普遍开展的竞聘上岗活动,就会引起项目中原有利益格局的变化,导致既得利益者与潜在利益者的矛盾,因为项目中某些成员由于掌控了各种资源、优势、好处而想维持现状,另一些人则希望通过变革在未来获取这些资源、优势和好处,并由此产生对抗和冲突。

4  目标差异。不同价值理念及成长经历的项目成员有着各自不同的奋斗目标,而且往往与项目目标不一致。同时,由于所处部门及管理层面的局限,成员在看待问题及如何实现项目目标上,也有很大差异,存在“屁股决定脑袋”的现象,并由此产生冲突。

n冲突的过程

以下的模型呈现出冲突的过程 

()项目中冲突的种类及特征

在项目管理中,冲突无时不在,从项目发生的层次和特征的不同,项目冲突可以分为:

人际冲突:是指群体内的个人之间的冲突,主要指群体内两个或两个以上个体由于意见、情感不一致而相互作用时导致的冲突。

群体或部门冲突:是指项目中的部门与部门、团体与团体之间,由于各种原因发生的冲突。

个人与群体或部门之间的冲突:不仅包括个人与正式组织部门的规则制度要求及目标取向等方面的不一致,也包括个人与非正式组织团体之间的利害冲突。

项目与外部环境之间的冲突:主要表现在项目与社会公众、政府部门、消费者之间的冲突。如社会公众希望项目承担更多的社会责任和义务,项目的组织行为与政府部门约束性的政策法规之间的不一致和抵触,项目与消费者之间发生的纠纷等等。

()项目中冲突对项目的影响

从冲突对项目的影响看,通常会被分为:建设性冲突和破坏性冲突

冲突类型

冲突带来的影响

建设性冲突

激发才干和能力

带动创新和改变

学习有效解决或避免冲突的方法

对项目的问题提供诊断信息

带来整合及同心协力

破坏性冲突

耗费时间

过渡展现自利倾向,妨碍组织整体的发展

持续的人际冲突带来个人情绪上和身心健康上的损害

转移且消耗组织的时间与能量

可能要付出高昂的经济和情绪上的代价

制造“我-他们对立”的态度

导致讯息错误和实施真相的扭曲

 20世纪40年代以来,冲突被普遍认为是有害无益的,强调管理者应尽可能避免和消除冲突。但近些年,这种观念有了很大的改变,人们意识到冲突在企业中存在的必然性和合理性,认为冲突并不一定会导致低效,建设性冲突有利于改变企业反应迟缓、缺乏创新的局面,提高企业效率。因此,企业有时需要建设性的冲突,管理者也需要在适当的时候激发一定水平的冲突。通用电气的CEO韦尔奇就十分重视激发冲突。他认为坦诚、建设性冲突能够让不同观点交锋,碰撞出新的思想火花,有利于管理者顺势推动改革与创新。在通用电气,韦尔奇经常与成员面对面的沟通、辩论,诱发同成员的冲突,从而不断发现问题,改进企业的管理。下图是冲突水平与项目绩效之间得关系。

同样在项目管理过程中,项目经理应该适当地利用建设性冲突,避免破坏性冲突,但这两种冲突是共生的,通常只是一线之差,项目经理能否应用得好也是管理艺术的体现。

()冲突管理中常用的处理方法

当冲突产生并有可能造成项目组织混乱、无序或分裂时,管理者需要采取措施削弱和解决冲突,一般可考虑以下策略:

策略一:回避和冷处理。管理者对所有的冲突不应一视同仁。当冲突微不足道、不值得花费大量时间和精力去解决时,回避是一种巧妙而有效的策略。通过回避琐碎的冲突,管理者可以提高整体的管理效率。尤其当冲突各方情绪过于激动,需要时间使他们恢复平静时,或者立即采取行动所带来的负面效果可能超过解决冲突所获得的利益时,采取冷处理是一种明智的策略。总之,管理者应该审慎地选择所要解决的冲突,不能天真地认为优秀的管理者就必须介入到每一个冲突中。

策略二:强调共同的战略目标。共同的战略目标的作用在于使冲突各方感到使命感和向心力,意识到任何一方单凭自己的资源和力量无法实现目标,只有在全体成员通力协作下才能取得成功。如企业中投资部门、经营管理部门、质量安全部门、销售部门等都会不知觉得强调自己部门得重要性,需要使其意识到要从企业整体高度看待问题,而不是从部门、甚至个人的角度。在这种情况下,冲突各方可能为这个共同的战略目标相互谦让或做出牺牲,避免冲突的发生。

策略三:制度的建立和执行。制度的存在虽然让许多人觉得受到约束,但它是一条警戒线,足以规范成员的作为。因此通过制定一套切实可行的制度并将企业成员的行为纳入到制度的规范范围,靠法治而不是人治来回避和降低冲突。

策略四:各方的妥协。联合国秘书长安南曾坦言:联合国作为全球政治冲突的中心,其最大的作用就是妥协。所谓妥协就是在彼此之间的看法、观点的交集基础上,建立共识,彼此都做出一定的让步,达到各方都有所赢、有所输的目的。当冲突双方势均力敌或焦点问题纷繁复杂时,妥协是避免冲突,达成一致的有效策略。

策略五:强制执行。这是同妥协相对立的解决方式,当管理者需要对重大事件做出迅速的处理时,或者需要采取不同寻常的行动而无法顾及其它因素时,以牺牲某些利益来保证决策效率也是解决冲突的途径之一。

 

此事发生之后,我苦思良久,认为其中涉及的不仅仅是冲突管理,应该还包括项目授权,团队建设以及其他沟通管理等。这里我也并不想讨论谁是谁非,项目管理中最为复杂的部分就是对人的管理,其他方面都可以通过制定标准和规则来限定,唯对人很难做到,各人的价值观不同,而且很难判断。两个个性迥异的人碰到一起,难免电光火石,很难糅合在一起。这里我又想到了在博客里看到一段话:管理自己的状态
  在管理冲突之前,你先要管理好自己。面对冲突,你的自然反应就是与之对抗、逃跑或者被吓呆。你可以通过控制自己的情绪或调整注意力,来克服这些恐惧心理。人有许多种状态。状态是感觉、思维、心理和行为的综合体,它在很大程度上决定了我们的行事方式。我们可以让自己从消极状态转变为积极状态,从畏惧转变为勇敢,并且不按直觉行事,而是直接与冲突对方面对面。
  要做到这一点,关键是要通过心灵之眼,即尽力设想,来控制自己的注意力,这是大脑最强大的功能之一。它形成了我们对特定境况的看法,并决定了我们将如何反应。众所周知,运动员就是通过设想自己获得成功后的情形以及永不放弃目标的信念,而不断提高成绩的。
  在冲突管理中,决定结果是正面还是负面的便是心灵之眼。它是基于一个人的经历和选择而塑造出来的,在很大程度上决定了我们的世界观,并最终决定我们在冲突管理中的成败。许多经理人在面对冲突时都成为了恐惧心理或其他消极情绪的奴隶,从而错失了解决冲突的机会。
  管理学教授内克(Chris Neck)和门茨(Charles Manz)指出,我们可以通过与内心的对话来改变对某种境况的看法告诉自己某件事情是个机会,而不是阻碍。这与半杯水的道理是一样的,即从半空的杯子到半满的杯子的转变。把对手看作是潜在的盟友,并与其接近,通过求同存异的方式建立关系纽带。

转载于:https://www.cnblogs.com/laoyi/archive/2012/03/24/2415363.html

``` #include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define LL long long const int mo=998244353; struct edg{int y,nxt;}e[51000]; int lk[110000],ltp=0; void ist(int x,int y){ e[++ltp]=(edg){y,lk[x]}; lk[x]=ltp;} struct nds{int x,y;}a[51000]; int n,m,o; int q[110000],hd=0; int c[110000],ctp=0; LL f[2][110000]; int bnrsch(int x){ int l=1,r=ctp,md; while(l+1<r){ md=(l+r)>>1; (c[md]<x ? l : r)=md; } return c[l]==x ? l : r; } int main(){ cin>>n>>m>>o; for(int i=1;i<=o;++i){ scanf("%d%d",&a[i].x,&a[i].y); q[++hd]=a[i].x,q[++hd]=a[i].y; } sort(q+1,q+hd+1); for(int i=1;i<=hd;++i)if(q[i]!=q[i-1]) c[++ctp]=q[i]; for(int i=1;i<=o;++i)if(a[i].y!=a[i].x) ist(bnrsch(a[i].y),bnrsch(a[i].x)); if(c[1]!=1){ c[++ctp]=1; f[0][ctp]=1; } else f[0][1]=1; for(int i=1;i<=m;++i){ LL bwl=0; for(int j=1;j<=ctp;++j) bwl=(bwl+f[(i-1)&1][j])%mo; bwl=(bwl+f[(i-1)&1][0]*(n-ctp))%mo; for(int j=0;j<=ctp;++j){ f[i&1][j]=(bwl-f[(i-1)&1][j])%mo; for(int k=lk[j];k;k=e[k].nxt) f[i&1][j]=(f[i&1][j]-f[(i-1)&1][e[k].y])%mo; } } printf("%lld\n",(f[m&1][(c[1]==1 ? 1 : ctp)]%mo+mo)%mo); return 0; }```# P5888 传球游戏 ## 题目背景 羊城有善蹴鞠者。会足协之杯,于校园之东北角,施两球场,蹴鞠者站球场中,$n$ 人,一球,二门,三裁判而已。观众团坐。少倾,但闻球场中哨声一响,满坐寂然,无敢哗者。 当是时,传球声,微微风声,队员疾跑声,教练呼喊声,拉拉队助威声,一时齐发,众妙毕备。满场观众无不伸颈,侧目,微笑,默叹,以为妙绝。 未几,我球员施一长传,彼球员截之,望我龙门冲来。 但见守门员 oql 立于门,若有所思—— ## 题目描述 **原来他在想这么一个问题:** 场上的 $n$ 个球员围成一圈,编号从 $1$ 到 $n$ ,刚开始球在 $1$ 号球员手中。一共 $m$ 次传球,每次传球必须传给一个人,但不能传到自己手中。求第 $m$ 次传球以后传回 $1$ 号球员的方案数。 但他觉得这个问题太简单了,于是加了 $k$ 条限制,每条限制形如 $a,b$,表示 $a$ 号球员不能将球传给 $b$ 号球员。 为了使得 oql 的注意力转移回球场上,你需要在最短的时间内告诉他这个方案数是多少。 你只需要告诉他答案对 $998244353$ 取模后的结果。 ## 输入格式 输入数据包括 $k+1$ 行: 第一行三个整数 $n,m,k$,分表代表球员数,传球次数,限制条数。 接下来 $k$ 行,每行两个整数 $a_i,b_i$,表示 $a_i$ 号球员不能将球传给 $b_i$ 号球员。 数据保证不会出现不同的 $i,j$ 使得 $a_i=a_j$ 且 $b_i=b_j$。 ## 输出格式 输出一个整数,表示 $m$ 轮后传回 $1$ 号球员的合法方案数对 $998244353$ 取模后的结果。 ## 输入输出样例 #1 ### 输入 #1 ``` 2 1 0 ``` ### 输出 #1 ``` 0 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 3 3 0 ``` ### 输出 #2 ``` 2 ``` ## 输入输出样例 #3 ### 输入 #3 ``` 7 13 5 1 3 4 5 5 4 6 1 2 2 ``` ### 输出 #3 ``` 443723615 ``` ## 说明/提示 对于 $10\%$ 的数据,$k=0$。 对于另外 $15\%$ 的数据,$n\leq 500$。 对于另外 $20\%$ 的数据,$n\leq 5\times 10^4$。 对于另外 $20\%$ 的数据,$k\leq 300$。 对于 $100\%$ 的数据,$1\leq n\leq 10^9$,$0\leq m\leq 200$,$0\leq k \leq \min(n\times(n-1),5\times 10^4)$,$1\leq a_i,b_i\leq n$,**不保证 $a_i,b_i$ 不相等**。 给代码加上注释
最新发布
03-29
<think>好的,我现在需要给用户提供的C++代码加上注释,并且解释这道题的解题思路。首先,我得仔细阅读题目和代码,理解其中的逻辑。 题目是传球游戏的变种,有n个球员围成圈,初始球在1号,经过m次传球,每次不能传给自己,并且有k条限制,某些a不能传给b。最终求传回1号的方案数。数据范围中n可以很大到1e9,但m只有200,k是5e4左右。所以显然不能用O(nm)的DP,必须找到优化方法。 看代码的结构,首先处理输入,把所有的a和b离散化,因为n很大,但实际出现的节点可能不多。代码里用了结构体数组存储限制,然后对所有的节点进行排序和去重,建立映射。这样可以将节点压缩到较少的数量,比如ctp个。 然后,动态规划的部分用了两个数组f[0]和f[1],可能是在交替使用,节省空间。这里的状态定义应该是f[i][j]表示第i次传球后,球在节点j的方案数。因为m是200,所以状态数是200*ctp,ctp可能最多是2k左右(每个限制的两个点),所以总状态数不大。 接下来,处理初始状态。如果1号没有在离散化的数组中,需要特别处理。比如,代码里有一个判断c[1]是否为1,如果不是的话,就将1号加入c数组的末尾,并初始化f[0][ctp]为1。否则直接初始化f[0][1]为1。这说明离散化后的节点可能不包括1号,所以需要单独处理。 然后,对于每次传球,计算新的状态。这里有一个bwl变量,累加上一轮所有可能的节点的方案数,然后乘以(n-ctp)可能指的是那些未被离散化的节点,因为这些节点之间没有限制,可以任意传(除了不能传给自己)。这部分可能对应的是那些没有出现在任何限制中的节点,他们之间的传递没有限制,除了不能传给自己。所以在计算的时候,这些未被离散化的节点可能被视为同一类,可以批量处理。 代码中的转移部分,对于每个j,新的f[i][j]等于总方案数减去上一轮的j的方案数(因为不能传给自己),再减去所有不能传给j的那些节点的方案数。这里的lk数组存储的是禁止的边,比如j不能接收来自某些节点的传球。所以在转移时,需要减去这些被禁止的情况。 最后,输出的是f[m][对应的1号的位置],根据离散化后的数组是否包含1号,决定取哪个位置的结果。 现在,我需要为代码加上注释,解释每个部分的功能。同时,要说明解题思路,可能包括离散化的处理、状态转移方程的推导,以及如何处理大量节点中的未被限制的节点。 可能的解题思路步骤: 1. 离散化处理:将所有出现过的a和b的值进行离散化,因为这些节点可能有特殊的限制,而其他节点之间可以自由传递(除了不能传给自己)。这一步将节点数压缩到O(k)级别。 2. 状态定义:f[i][j]表示第i次传球后,球在离散化后的第j个节点的方案数。同时,可能还有一个“其他”类,表示未被离散化的节点。 3. 转移方程:每个步骤的总方案数是所有可能的上一步节点之和,然后减去不能传给当前节点的限制。 4. 处理未被离散化的节点:这些节点之间可以互相传,但不能传给自己。所以它们的贡献可以被批量计算,例如总共有n - ctp个这样的节点,每个节点的贡献相同,所以可以乘以这个数量。 可能代码中的f数组中的0号位置是否用来表示未被离散化的节点?或者是否有其他处理方式? 看代码中的初始化部分,如果c[1]不等于1,那么会把1加入c数组,并初始化f[0][ctp] =1。否则,初始化f[0][1] =1。这里ctp是离散化后的节点数量。 在转移循环中,对于每个i,计算bwl为上一轮的总和(包括所有离散化后的节点)加上未被离散化的节点的贡献。未被离散化的节点的数量是n - ctp,每个未被离散化的节点的方案数是f[(i-1)&1][0]。所以bwl的总和是上一轮所有节点的总和加上未被离散化的节点总和乘以(n - ctp)。 然后,对于每个j,新的f[i][j]等于总方案数(bwl)减去上一轮j的方案数(因为不能传给自己),再减去所有禁止传递给j的节点的方案数。这可能是因为在总方案数中,每个节点可以传给除自己之外的任何节点,但如果有禁止的边,需要将这些边对应的贡献减去。 所以,这里的动态规划巧妙地处理了离散化和非离散化节点的情况,将复杂度降低到O(m*k),而k最多是5e4,所以可以处理m=200的情况。 现在,给代码加注释: 首先,头文件部分: #include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define LL long long const int mo=998244353; // 模数 结构体edg是邻接表,用来存储禁止的边: struct edg{int y,nxt;}e[51000]; // 邻接表,y是目标节点,nxt是下一条边 int lk[110000],ltp=0; // lk是链表头指针数组,ltp是边数 void ist(int x,int y){ e[++ltp]=(edg){y,lk[x]}; lk[x]=ltp;} // 插入边x->y 结构体nds存储限制中的a_i和b_i: struct nds{int x,y;}a[51000]; // 存储输入的每条限制 变量声明: int n,m,o; // o其实是k,题目中的k条限制 int q[110000],hd=0; // 用于离散化的数组,存储所有出现的x和y int c[110000],ctp=0; // 离散化后的数组,去重后的有序数组 LL f[2][110000]; // 滚动数组,f[i&1][j]表示第i次传球到j的方案数 离散化查找函数bnrsch: int bnrsch(int x){ // 在c数组中二分查找x的位置 int l=1,r=ctp,md; while(l+1<r){ md=(l+r)>>1; (c[md]<x ? l : r)=md; } return c[l]==x ? l : r; } 主函数: int main(){ cin>>n>>m>>o; // 输入n, m, k for(int i=1;i<=o;++i){ scanf("%d%d",&a[i].x,&a[i].y); q[++hd]=a[i].x,q[++hd]=a[i].y; // 收集所有出现的x和y } // 离散化处理 sort(q+1,q+hd+1); // 排序 for(int i=1;i<=hd;++i)if(q[i]!=q[i-1]) // 去重 c[++ctp]=q[i]; // 建立邻接表,存储禁止的边。注意这里可能处理的是离散后的节点 for(int i=1;i<=o;++i)if(a[i].y!=a[i].x) // 排除自环? ist(bnrsch(a[i].y),bnrsch(a[i].x)); // 插入边到被禁止的图中,这里可能要确认方向是否正确 // 初始化DP数组 if(c[1]!=1){ // 如果离散化后的数组第一个元素不是1,说明1号节点未被离散化,需要单独处理 c[++ctp]=1; // 将1号加入离散数组 f[0][ctp]=1; // 初始状态球在1号,对应离散后的位置是ctp } else f[0][1]=1; // 否则,初始位置是第一个元素 // DP转移 for(int i=1;i<=m;++i){ LL bwl=0; // 累计总方案数 // 计算上一轮所有离散节点的总和 for(int j=1;j<=ctp;++j) bwl=(bwl+f[(i-1)&1][j])%mo; // 处理未被离散化的节点:每个这样的节点有f[(i-1)&1][0]种方案,总共有n-ctp个 bwl=(bwl+f[(i-1)&1][0]*(n-ctp))%mo; // 转移每个节点j for(int j=0;j<=ctp;++j){ // j=0可能代表未被离散化的节点? // 新的状态j的方案数等于总方案数减去不能传给j的情况 f[i&1][j]=(bwl - f[(i-1)&1][j])%mo; // 不能传给自己,所以减去j自己的方案数 // 然后减去所有不能传给j的边对应的方案数 for(int k=lk[j];k;k=e[k].nxt) // 遍历所有禁止传递给j的节点y f[i&1][j]=(f[i&1][j] - f[(i-1)&1][e[k].y])%mo; } } // 输出结果,找到离散化后的1号的位置 int pos; if(c[1]==1) pos=1; // 如果离散数组第一个元素是1,则位置是1 else pos=ctp; // 否则是最后添加的那个元素(即之前添加的1号) printf("%lld\n",(f[m&1][pos]%mo+mo)%mo); // 处理负数取模 return 0; } 现在,需要理解为什么j的范围是0到ctp,其中j=0可能代表未被离散化的节点。例如,在初始化的时候,当1号不在离散数组中时,我们将其添加到c数组末尾,并初始化f[0][ctp] =1。而未被离散化的节点可能用j=0来表示? 在转移过程中,bwl的计算中,对于未被离散化的节点,每个节点可以传给其他所有未被离散化的节点中的(n-ctp-1)个,或者传给离散化的节点中的某些。但代码中的处理方式可能将未被离散化的节点视为一个整体,用f[i][0]来表示它们的总情况? 或者在代码中,j=0代表未被离散化的节点中的一个节点的状态?而总共有n-ctp个这样的节点,每个节点的状态转移方式相同,所以可以用一个变量来统一处理? 可能这里的f[i][j]中,j=0表示未被离散化的所有节点的集合。每个未被离散化的节点的行为是一致的,所以可以用一个变量来维护它们的总和。例如,在初始时,如果1号未被离散化(即不在c数组中),那么初始时球在1号节点,此时未被离散化的节点数量是n - (ctp-1),因为此时ctp是原来的ctp加上1号后的总数?或者可能这里的逻辑需要更仔细的推敲。 假设未被离散化的节点数量是n - ctp,因为这些节点没有被任何限制所涉及。在每次传球时,每个未被离散化的节点可以传给其他未被离散化的节点中的(n-ctp -1)个(因为不能传给自己),或者传给离散化的节点中的允许的节点。但是,由于未被离散化的节点之间没有限制,所以它们的传递方式可以批量处理。 但是代码中的处理方式似乎将未被离散化的节点的状态用f[i][0]来表示总和。例如,在计算bwl时,加上f[(i-1)&1][0]*(n-ctp),这可能意味着每个未被离散化的节点的方案数都是f[(i-1)&1][0],而总共有n-ctp个这样的节点,所以总贡献是它们的总和。 这可能意味着,对于未被离散化的节点,它们的转移行为是一致的,所以可以将它们视为一个整体,从而用j=0来表示它们的总和。比如,f[i][0]表示在第i次传球后,球在任何一个未被离散化的节点的方案数总和。或者,可能每个未被离散化的节点的方案数是相同的,所以可以用f[i][0]表示每个未被离散化的节点的方案数,总和是乘以数量。 这可能涉及到状态定义的优化,将未被离散化的节点视为一个等价类,因为它们之间的转移没有限制,所以它们的方案数可以统一计算。 综上,代码的思路是通过离散化处理,将出现过的节点压缩到c数组中,并处理禁止的边。未被离散化的节点被视为一个整体,可以批量处理。动态规划的状态转移中,计算总方案数时,分开处理离散化和未被离散化的节点,利用滚动数组优化空间。 现在,按照要求给代码加上注释,并解释解题思路。</think>### 解题思路 该题需要处理高维度数据下的动态规划问题,主要采用离散化与状态压缩技巧。以下是关键步骤: 1. **离散化处理** 将所有出现过的球员编号(包括限制中的$a_i$和$b_i$)排序去重,映射到连续的小整数。未被离散化的节点(即未出现在任何限制中的球员)视为等价类统一处理。 2. **动态规划定义** - `f[i][j]` 表示传球`i`次后球在离散化节点`j`的方案数。 - `j=0` 表示球在任意一个未被离散化的节点(这类节点行为一致,可批量计算)。 3. **转移方程** 每次传球的总方案数为所有可能来源减去非法传递: - **总方案数**:上一轮所有节点的方案数之和(包括离散化和未被离散化的节点)。 - **减法项**:不能传给自己(减去`f[i-1][j]`)和所有禁止的边(遍历邻接表减去对应贡献)。 4. **优化未被离散化节点** 这些节点数量为$n - \text{ctp}$,它们之间可以自由传递(仅不能传给自己),因此贡献可通过乘法批量计算。 ### 代码注释 ```cpp #include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define LL long long const int mo=998244353; // 模数 struct edg{int y,nxt;}e[51000]; // 邻接表存储禁止的边 int lk[110000],ltp=0; // lk为头指针数组,ltp为边数 void ist(int x,int y){ e[++ltp]=(edg){y,lk[x]}; lk[x]=ltp;} // 插入边x->y struct nds{int x,y;}a[51000]; // 存储输入的每条限制 int n,m,o; // n球员数,m传球次数,o限制数 int q[110000],hd=0; // 离散化临时数组 int c[110000],ctp=0; // 离散化后的有序唯一数组 LL f[2][110000]; // 滚动DP数组,f[i&1][j]表示i次传球到j的方案数 // 二分查找x在c数组中的位置 int bnrsch(int x){ int l=1,r=ctp,md; while(l+1<r){ md=(l+r)>>1; (c[md]<x ? l : r)=md; } return c[l]==x ? l : r; } int main(){ cin>>n>>m>>o; // 收集所有涉及的节点 for(int i=1;i<=o;++i){ scanf("%d%d",&a[i].x,&a[i].y); q[++hd]=a[i].x, q[++hd]=a[i].y; // 存入离散化数组 } // 离散化处理 sort(q+1,q+hd+1); for(int i=1;i<=hd;++i) if(q[i]!=q[i-1]) c[++ctp]=q[i]; // 去重得到唯一数组c // 构建邻接表:禁止传递的边(反向存储,便于后续处理) for(int i=1;i<=o;++i) if(a[i].y!=a[i].x) // 排除自环 ist(bnrsch(a[i].y), bnrsch(a[i].x)); // 注意此处为反向建边 // 初始化:球初始在1号球员 if(c[1]!=1){ // 1号未被离散化,需手动添加 c[++ctp]=1; f[0][ctp]=1; // 初始状态在ctp位置 } else { f[0][1]=1; // 初始状态在位置1 } // DP转移 for(int i=1;i<=m;++i){ LL bwl=0; // 累计总方案数 // 计算上一轮离散化节点的总贡献 for(int j=1;j<=ctp;++j) bwl = (bwl + f[(i-1)&1][j]) % mo; // 加上未被离散化节点的贡献(每个节点贡献相同,乘以数量) bwl = (bwl + f[(i-1)&1][0]*(n-ctp)) % mo; // 更新当前轮次的状态 for(int j=0;j<=ctp;++j){ // 初始化为总方案数,减去不能传给自己的部分 f[i&1][j] = (bwl - f[(i-1)&1][j]) % mo; // 遍历所有禁止传递到j的边,减去对应贡献 for(int k=lk[j];k;k=e[k].nxt) f[i&1][j] = (f[i&1][j] - f[(i-1)&1][e[k].y]) % mo; } } // 找到1号在离散化数组中的位置 int pos = (c[1]==1) ? 1 : ctp; // 处理负数取模并输出 printf("%lld\n", (f[m&1][pos]%mo + mo) % mo); return 0; } ``` ### 关键点解释 - **离散化**:将可能涉及的所有节点压缩为小范围整数,减少计算量。 - **滚动数组**:交替使用两个一维数组,将空间复杂度从$O(mn)$降至$O(n)$。 - **批量处理未被离散化节点**:利用其行为一致性,通过乘法快速计算总贡献。 - **反向建边**:存储“不能传给j的节点”,便于在转移时快速扣除非法方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值