• 【FCS NOI2018】福建省冬摸鱼笔记 day5


    第五天,也是讲课的最后一天。

    数据结构专题,讲师:杨志灿

    他的blog我似乎找不到了……以前肯定是在百度博客里面。但是现在百度博客消失了。

    PPT做的很有感觉,说了很多实用的技巧。

    我觉得其实是收获最大的一天,因为听懂了XD


    中午划水


    下午的题也非常良心,然而@ghostfly233@Melacau说他们做过原题???

    问题就是非常卡常!n=10^5的nlogn题目,400组询问,4秒?6亿复杂度啊!

    然而就是跑过了,我也没办法,树状数组复杂度不满……

    矩阵快速幂也卡……非常难受。

    【T1】

    题意:有(1leq aleq bleq cleq n),问多少个((a,b,c))三元组满足(a+b^2equiv c^3(mod;k))。

    题解:枚举b,发现a+b^2的范围是b^2+1到b^2+b是连续的一段,考虑计算这段中的c大于等于b的c^3的个数。

    显然用树状数组维护,复杂度有点问题,可是就是AC了XD。代码非常的优美。

    #include<cstdio>
    #include<cstring>
    int n,k,bit[100001];
    long long ans;
    inline void I(int i){for(++i;i<=k;i+=i&-i)++bit[i];}
    inline int Q(int i){int s=0;for(++i;i;i-=i&-i)s+=bit[i];return s;}
    int main(){
    	freopen("exclaim.in","r",stdin);
    	freopen("exclaim.out","w",stdout);
    	int T; scanf("%d",&T);
    	for(int t=1;t<=T;++t){
    		scanf("%d%d",&n,&k); ans=0;
    		memset(bit,0,sizeof bit);
    		int sum=0;
    		for(int i=n;i;--i){
    			I(1ll*i*i*i%k); ++sum;
    			long long l=1ll*i*i+1, r=1ll*i*i+i;
    			ans+=1ll*(r/k-l/k)*sum+Q(r%k)-Q(l%k-1);
    		}
    		printf("Case %d: %lld
    ",t,ans);
    	}
    	return 0;
    }
    

    【T2】

    题意:你玩一个多结局RPGgalgame(???),游戏流程类似树形结构,你总共通关了k次,每个场景你都会获得该场景的价值(可能还有好感度和浪费手纸?),但是多次经历该场景就只有一次价值了。

    价值都是正整数。

    问他怎样攻略能获得最大价值?

    题解:有一个贪心思路:每次都走最大价值的路线,这样一定最终价值也最大。

    如何证明?

    我们先证明最大的一定要选吧,这样后面的归纳一下就完了。

    先分类讨论:

    假设我们没有选择最大的,那么我们从最大的这条路线往回回溯,遇到第一个有选取的场景,那么走这个场景的这条(任意一条)路线,都可以换成走最大的路线。

    为什么?假设走到这个场景的路线只有一条,那么显然更优,因为前面的价值都一样,换成这条会更优。

    假设有多条,那么其中任意一条换掉之后,多了最优的这一条的价值收益,并且自己这条的收益还不用完全减掉,因为还有其他的路线走过,所以比上一种情况还来得优。

    那么我们就证明了,最大的路线一定要选,接下来把最大的路线的收益删去,变成0,那么这时再选取最大的还是一样的。于是证明完毕。

    那么如何处理呢?包括查询最大收益和修改。

    其实这就是一个线段树能够维护的:

    把节点按照DFS序排序,一个点的所有孩子就是一段区间。

    把所有的叶子节点(结局)按照DFS序扔进线段树,初始值就是路线收益,查询时直接查最大值,修改时,我们知道一个点的孩子对应一段区间,所以区间减法即可。

    查询k次,每次O(1),修改最多n次,每次O(log 叶子节点数)。

    那么这题就算做完了。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,k,a[200001],fa[200001],t[200001],cnt;
    int h[200001],to[200001],nxt[200001],tot;
    inline void ins(int x,int y){nxt[++tot]=h[x];to[tot]=y;h[x]=tot;}
    int L[200001],R[200001],vis[200001];
    long long dat[524289],lazy[524289],ans;
    int pos[524289];
    void dfs(int u){
    	if(!h[u]) {L[u]=R[u]=++cnt, t[cnt]=u; return;}
    	L[u]=n; for(int i=h[u];i;i=nxt[i]) dfs(to[i]), L[u]=min(L[u],L[to[i]]), R[u]=max(R[u],R[to[i]]);
    }
    inline void pushdown(int i){
    	lazy[i<<1]+=lazy[i];
    	lazy[i<<1|1]+=lazy[i];
    	dat[i<<1]+=lazy[i];
    	dat[i<<1|1]+=lazy[i];
    	lazy[i]=0;
    }
    void build(int i,int l,int r){
    	pos[i]=l;
    	if(l==r) return;
    	int mid=l+r>>1;
    	build(i<<1,l,mid), build(i<<1|1,mid+1,r);
    }
    void Ins(int i,int l,int r,int a,int b,int v){
    	if(a<=l&&r<=b) {dat[i]+=v; lazy[i]+=v; return;}
    	if(r<a||b<l) return;
    	pushdown(i); int mid=l+r>>1;
    	Ins(i<<1,l,mid,a,b,v); Ins(i<<1|1,mid+1,r,a,b,v);
    	dat[i]=max(dat[i<<1],dat[i<<1|1]);
    	pos[i]=dat[i<<1]>=dat[i<<1|1]?pos[i<<1]:pos[i<<1|1];
    }
    int main(){
    	freopen("game.in","r",stdin);
    	freopen("game.out","w",stdout);
    	scanf("%d%d",&n,&k); k=min(k,n);
    	for(int i=1;i<=n;++i) scanf("%d",a+i);
    	for(int i=1,x,y;i<n;++i) scanf("%d%d",&x,&y), ins(x,y), fa[y]=x;
    	dfs(1);
    	build(1,1,cnt);
    	for(int i=1;i<=n;++i) Ins(1,1,cnt,L[i],R[i],a[i]);
    	for(int i=1;i<=k;++i){
    		int p=t[pos[1]];
    		ans+=dat[1];
    		int sum=0;
    		while(p&&!vis[p]) Ins(1,1,cnt,L[p],R[p],-a[p]), vis[p]=1, sum+=a[p], p=fa[p];
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    【T3】

    题意:求有向图中长度小于k的环的个数,没有自环。

    题解:简单矩阵,有著名定理支持:邻接矩阵的k次方就是从一个节点出发,经过k步到达另一个节点的路径数。

    即求(A^1+A^2+cdots+A^{k-1})。

    简单矩阵快速幂即可,加一个稀疏矩阵优化,跑的飞快。根本不需要分治。

    #include<cstdio>
    #include<cstring>
    int n,n2,k,p,ans;
    struct Mat{int fk[200][200];}M1,M2;
    inline Mat operator*(Mat p1,Mat p2){
    	Mat p3;memset(p3.fk,0,sizeof p3.fk);
    	for(register int i=0;i<n2;++i)
    	for(register int k=0;k<n2;++k) if(p1.fk[i][k])
    	for(register int j=0;j<n2;++j)
    		p3.fk[i][j]=(p3.fk[i][j]+1ll*p1.fk[i][k]*p2.fk[k][j])%p;
    	return p3;
    }
    int main(){
    	freopen("tour.in","r",stdin);
    	freopen("tour.out","w",stdout);
    	scanf("%d",&n); n2=n<<1;
    	char ch;
    	for(register int i=0;i<n;++i) M1.fk[i][i]=M2.fk[i][i]=M2.fk[i+n][i+n]=1;
    	for(register int i=n;i<n2;++i) for(register int j=0;j<n;++j) (ch=getchar())=='Y'?M1.fk[i][j]=M1.fk[i][j+n]=1:(ch!='N'?--j:0);
    	scanf("%d%d",&k,&p); --k;
    	if(p==1){puts("0");return 0;}
    	while(k){
    		if(k&1) M2=M2*M1;
    		k>>=1; M1=M1*M1;
    	}
    	for(register int i=0;i<n;++i) ans=(ans+M2.fk[i+n][i])%p;
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    c#将 1, 2, ..., 9共 9 个数字分成 3 组
    信息学院本科生创新项目总结
    Element-ui的使用
    fastmock接口管理
    mock安装与使用
    开闭原则
    里氏替换原则
    依赖倒置原则
    接口隔离原则
    单一职责原则
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/8456947.html
Copyright © 2020-2023  润新知