https://www.luogu.org/contest/show?tid=1614
TB P3654 First Step (ファーストステップ)
https://www.luogu.org/problem/show?pid=3654
题目描述
可是……这个篮球场,好像很久没有使用过的样子啊……
里面堆满了学校的各种杂物呢……
我们Aqours的成员要怎么在里面列队站下呢?
我们浦之星女子学院的篮球场是一个R行C列的矩阵,其中堆满了各种学校的杂物 (用"#"表示),空地 (用"."表示) 好像并不多的样子呢……
我们Aqours现在已经一共有K个队员了,要歌唱舞蹈起来的话,我们得排成一条1*K的直线,一个接一个地站在篮球场的空地上呢 (横竖均可)。
我们想知道一共有多少种可行的站位方式呢。
Aqours的真正的粉丝的你,能帮我们算算吗?
输入输出格式
输入格式:
第一行三个整数 R, C, K。
接下来的R行C列,是浦之星女子学院篮球场。
输出格式:
总共的站位方式数量。
输入输出样例
5 5 2 .###. ##.#. ..#.. #..#. #.###
8
说明
R C K 备注
1-2 <=10 <=10 <=min(R,C) 无
3-4 <=100 <=100 1 无
5-6 <=100 <=100 <=min(R,C) 没有障碍
7-10 <=100 <=100 <=min(R,C) 无
横着、竖着各遍历一遍数据,每个连续空地长度为l,则答案加上l-k+1
注意:当k=1时,两次遍历结果一样,所以要将答案除2,或开始特判k=1
因为忽略了k=1所以第一次提交80
#include#include using namespace std; int r,c,k; int a[102][102]; int main() { scanf("%d%d%d",&r,&c,&k); char ch; for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) { cin>>ch; if(ch=='.') a[i][j]=1; } int ans=0,now=0; if(k==1) { for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) { if(a[i][j]) ans++; } printf("%d",ans); return 0; } for(int i=1;i<=r;i++) { now=0; for(int j=1;j<=c+1;j++) { if(a[i][j]==1) now++; else { ans+=now-k+1>0 ? now-k+1:0; now=0; } } //if(a[i][c]) ans+=now-k+1>0 ? now-k+1:0; } for(int j=1;j<=c;j++) { now=0; for(int i=1;i<=r+1;i++) { if(a[i][j]==1) now++; else { ans+=now-k+1>0 ? now-k+1:0; now=0; } } //if(a[r][j]) ans+=now-k+1>0 ? now-k+1:0; } printf("%d",ans); }
TC P3655 不成熟的梦想家 (未熟DREAMER)
https://www.luogu.org/problem/show?pid=3655
题目描述
我们Aqours的成员共有N+1人,他们会列成一队。
他们的唱功以A[0]到A[N]表示,A[i]均给出。
学园都市的机器可以改变队列中连续多个成员的唱功值,并将其加上一个数Z,当然当Z是负数的时候就变成减去了。
我打算一共使用这个机器Q次,每次把第X到第Y号()的成员都加上Z点唱功值。
而我们队伍的魅力值B,是这么算的:
一开始B=0,然后从第1号到第N号成员,
-
当:
- 当:
其中S和T是LoveLive组委会给我们的常数。
果然,我是バカチカ(笨蛋千歌)呢,所以作为领导我永远排在队伍的开头,唱功永远是0,机器也不会改到我头上呢。
你能帮我们算算,我每次使用完这个机器之后,成员的魅力B是多少吗?
输入输出格式
输入格式:
第一行4个整数,N,Q,S,T,各个变量在描述中已经解释
接下来N+1行,每行一个数整数Ai,其中A0=0
接下来Q行,每行3个整数,X,Y,Z各个变量在描述中已经解释
输出格式:
Q个整数,表示答案。
输入输出样例
4 3 2 3 0 5 2 4 6 1 2 1 3 4 -3 1 4 2
-9 -1 -5
说明
30% 的数据 ,
另外20% 的数据
100%的数据 ;;
请注意可能需要使用int64,cin/cout可能超时。
样例解释:
第一次变化后,
A 0 6 3 4 6
B -12 -3 -5 -9
一、
乍一看,递推式,莫非要动态维护递推关系?
没那么复杂
再仔细看看
虽然后一个b是由前一个b递推出来的
但关系式是A+B形式
A的结果不影响B的结果
A是前面所有的结果之和
那么把每个B看做相对独立的部分
Ai+Bi=B1——i-1+Bi
所以,可以单独维护每个S|Ai-1 - Ai|、T|Ai-1 - Ai|
累加结果就好
二、
观察式子,可以发现对b有影响的是两个数之间的差
修改是区间修改,也就是说
如果用f[i]记录第i个数与第i-1个数的差
只有区间左端点、右端点+1的f是修改了的
三、
1、官方解法 1422ms / 3.43MB
所以先算出没有修改之前的b
然后对于每一个修改操作
减去区间l,r+1原来的S|Ai-1 - Ai|或T|Ai-1 - Ai|
加上新的S|Ai-1 - Ai|或T|Ai-1 - Ai|
#includeusing namespace std; int n,m,s,t,a[200001]; long long f[200001],b; int main() { scanf("%d%d%d%d",&n,&m,&s,&t); scanf("%d",&a[0]); int x,k,y,z,p; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); f[i]=a[i]-a[i-1]; k=f[i]>0 ? -s : -t; b+=k*f[i]; } for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); k=f[x]>0 ? -s : -t; b-=k*f[x]; f[x]+=z; k=f[x]>0 ? -s : -t; b+=k*f[x]; if(y!=n) { k=f[y+1]>0 ? -s: -t; b-=k*f[y+1]; f[y+1]-=z; k=f[y+1]>0 ? -s : -t; b+=k*f[y+1]; } printf("%lld\n",b); } }
2、个人解法 4980ms / 39.87MB
线段树单点修改、维护区间和
#includeusing namespace std; int n,q; long long s,t,last=0; struct node { int l,r; long long dis,key,f; }tr[200001*4]; void build(int k,int l,int r) { tr[k].l=l;tr[k].r=r; if(l==r) { scanf("%lld",&tr[k].key); if(tr[k].key>last) tr[k].dis=s*(last-tr[k].key); else tr[k].dis=t*(last-tr[k].key); last=tr[k].key; return; } int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); tr[k].dis=tr[k<<1].dis+tr[k<<1|1].dis; } void down(int k) { int l=k<<1,r=k<<1|1; if(tr[l].l==tr[l].r) tr[l].key+=tr[k].f; else tr[l].f+=tr[k].f; if(tr[r].l==tr[r].r) tr[r].key+=tr[k].f; else tr[r].f+=tr[k].f; tr[k].f=0; } void add(int k,int l,int r,int w) { if(tr[k].l>=l&&tr[k].r<=r) { if(tr[k].l==tr[k].r) tr[k].key+=1ll*w; else tr[k].f+=1ll*w; return; } if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1; if(l<=mid) add(k<<1,l,r,w); if(r>mid) add(k<<1|1,l,r,w); } long long query(int k,int x) { if(tr[k].l==tr[k].r) return tr[k].key; if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1; if(x<=mid) return query(k<<1,x); else return query(k<<1|1,x); } void change(int k,int x) { if(tr[k].l==tr[k].r) { last=query(1,x-1); if(tr[k].key>last) tr[k].dis=s*(last-tr[k].key); else tr[k].dis=t*(last-tr[k].key); return; } if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1; if(x<=mid) change(k<<1,x); else change(k<<1|1,x); tr[k].dis=tr[k<<1].dis+tr[k<<1|1].dis; } int main() { scanf("%d%d%lld%lld",&n,&q,&s,&t); build(1,0,n); int x,y,z; for(int i=1;i<=q;i++) { scanf("%d%d%d",&x,&y,&z); add(1,x,y,z); change(1,x); if(y!=n) change(1,y+1); printf("%lld\n",tr[1].dis); } }
一个bug:
错误的认为线段树中i号叶子节点的前一个叶子节点是i-1号
所以找Ai-1的时候,直接调用的结构体i-1的值
不对,发现错误:
i-1号叶子节点前面可能有未传下来的标记,所以又搞了一次标记下传
还不对
根源在于只有当线段树的形态是完全二叉树的时候,i号叶子节点前一个叶子节点是i-1号
自己做的时候一直在想怎么维护递推式,没有发现递推式可以拆开
TD P3656 展翅翱翔之时 (はばたきのとき)
https://www.luogu.org/problem/show?pid=3656
题目描述
不过,好像中继卫星上,出了一些问题呢……
我们的中继卫星一共有N颗,编号成1到N。不过,好像一个中继卫星可以且仅可以单向地从另一颗中继卫星那儿接收数据。
第i颗卫星现在已经被设定到了从第Ai颗卫星 (称为接收源) 那儿接受数据。
不过这些中继卫星的接收源是可以修改的,只不过每次修改要花一定的资金呢。
听说要达成中继的话,这些卫星之间必须两两之间能够互相(直接或间接)通信才行啊。
虽然鞠莉家里很有钱,可是这么大的花费,也得提前准备一下呢。
所以,你能帮我们算算这样子一共最少要花多少钱吗?
输入输出格式
输入格式:
第一行1个整数N。
接下来N行,每行2个整数Ai,Ci,表示初始时,第i个中继卫星从第Ai颗卫星处接收数据,以及该卫星调整接收源的所需花费。
输出格式:
输出一个整数,表示鞠莉所需准备的最小的花费。
输入输出样例
4 2 2 1 6 1 3 3 1
5
说明
10% N<=10
40% N<=15
70% N<=3000
100% 2<=N<=100000, 1<=Ci<=10^9
官方题解:
我们把这种每个点入度(或者出度)为1的点称为有向环套树。显然,给出的图是一个环套树森林。我们讨论单个环套树的情况。
如果要两两可以互相交互,那么就是形成强连通。
只有一种结构可以达成:环。
所以说,我们需要找找出每个环套树的最长链。
首先找出环,然后环上的树上有多个孩子的点贪心保留权值大的点,这样变成了外向链。
然后枚举环上的每个点,把这个环破开,然后计算最长的链(你可以想象一下“6”这个数字,对就是这个意思)。
森林可能不连通,所以分别处理。最后复杂度是
表示看不懂
#include#include using namespace std; #define N 100001 int a[N],b[N],n; long long c[N],ans; long long m1[N],m2[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&c[i]); for(int i=1;i<=n;i++) b[i]=-1; bool f=0; for(int i=1;i<=n;i++) { if(b[i]!=-1) continue; int j=i; while(b[j]==-1) { b[j]=i;j=a[j]; } if(b[j]==i) { int d=0; while(b[j]!=-2) { b[j]=-2; j=a[j]; d++; } if(d==n) f=1; } } if(!f) { for(int i=1;i<=n;i++) ans+=c[i]; for(int i=1;i<=n;i++) { m1[a[i]]=max(m1[a[i]],c[i]); if(b[i]!=-2) m2[a[i]]=max(m2[a[i]],c[i]); } for(int i=1;i<=n;i++) ans-=m1[i]; for(int i=1;i<=n;i++) { if(b[i]!=-2) continue; long long m=1e10; int j=i; while(b[j]==-2) { m=min(m,m1[j]-m2[j]); b[j]=-3; j=a[j]; } ans+=m; } } printf("%lld",ans); }
自己写费用流,得了20分
#include#include #include #define N 101 using namespace std; int n,tot=1; int front[N*2],from[N*N],nextt[N*N],to[N*N],cap[N*N],cost[N*N]; int src,dec; int pre[N*2]; long long dis[N*2],ans; bool v[N*2]; queue<int>q; void add(int u,int v,int w,int c) { from[++tot]=u;to[tot]=v;nextt[tot]=front[u];front[u]=tot;cap[tot]=w;cost[tot]=c; from[++tot]=v;to[tot]=u;nextt[tot]=front[v];front[v]=tot;cap[tot]=0;cost[tot]=-c; } bool spfa() { while(!q.empty()) q.pop(); for(int i=1;i<=dec;i++) dis[i]=2e15; q.push(src);dis[src]=0;v[src]=true; int t; while(!q.empty()) { int now=q.front();q.pop();v[now]=false; for(int i=front[now];i;i=nextt[i]) { t=to[i]; if(dis[t]>dis[now]+1ll*cost[i]&&cap[i]>0) { dis[t]=dis[now]+1ll*cost[i]; pre[t]=i; if(!v[t]) { v[t]=true; q.push(t); } } } } return dis[dec]!=2e15; } int main() { scanf("%d",&n); int x,y; dec=(n+1)<<1; for(int i=1;i<=n;i++) add(src,i<<1,1,0); for(int i=1;i<=n;i++) { scanf("%d%d",&x,&y); add(i<<1,x<<1|1,1,0); for(int j=1;j<=n;j++) if(j!=x) add(i<<1,j<<1|1,1,y); } for(int i=1;i<=n;i++) add(i<<1|1,dec,1,0); int tmp; while(spfa()) { tmp=0x7fffffff; for(int i=pre[dec];i;i=pre[from[i]]) tmp=min(tmp,cap[i]); for(int i=pre[dec];i;i=pre[from[i]]) { cap[i]-=tmp;cap[i^1]+=tmp; } ans+=dis[dec]; } printf("%lld",ans); }