zoukankan      html  css  js  c++  java
  • CSP模拟赛20190922

    @


    两天的一二题都挺水的,期望本来可以得400,但是没有一天是得完了能得的分的。

    DAY1

    T1

    优美的字符串(string)
    【题目描述】
    小 Y 送给小 F 一个字符串作为礼物,这个字符串只由’a’ 和’b’ 组成。
    由于小 F 患有严重的强迫症,他觉得这个字符串并不优美,他决定对它做一些
    操作:
    每次操作从字符串中选择一个’ab’ 子串,并将其替换为’bba’。
    如果一个字符串的所有’b’ 都在所有’a’ 前面,他认为这个字符串是优美的。
    现在小 F 想知道,最少需要多少次操作,能使这个字符串是优美的,或者这个字
    符串不可能变成优美的。
    【输入格式】
    从文件 string.in 中读入数据。
    一行一个只由’a’ 和’b’ 组成的字符串。
    【输出格式】
    输出到文件 string.out 中。
    输出一行一个整数,如果无解,输出“-1”。否则输出最少操作次数对.
    1000000007
    取. 模. 。

    大水题,手推五分钟就切了。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=1000007;
    const ll mod=1000000007;
    ll dis[N];
    char c[N];
    template<class T>inline void read(T &res){
    	static char ch;T flag=1;
    	while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
    	while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
    }
    int main()
    {
    	freopen("string.in","r",stdin);
    	freopen("string.out","w",stdout);
    	scanf("%s",c);
    	int n=strlen(c),tot=0;
    	ll ans=0;
    	c[n]='a';
    	for(int i=n-1,last=n;i>=0;--i)
    		if(c[i]=='a'){
    			dis[++tot]=last-i-1;
    			last=i;
    		}
    	for(int i=1;i<=tot;++i){
    		ans=(ans+dis[i])%mod;
    		dis[i+1]=(dis[i]*2+dis[i+1])%mod;
    	}
    	printf("%lld\n",ans);
        return 0;
    }
    

    T2

    【题目描述】
    小 X 同学有很强的计算能力,现在他正在玩一个游戏。
    现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的
    个数。
    小 X 不断的进行操作,直到这个数变成 1 为止。
    由于小 X 的计算能力很强,他现在给出一个 n,他想知道有多少不超过 n 的正整
    数会在 k 次操作后变成 1。由于答案可能很大,请对 1000000007 取模。
    【输入格式】
    从文件 number.in 中读入数据。
    第一行一个用二进制表示的正整数 n,含义如题目描述。
    第二行一个整数 k, 含义如题目描述。
    【输出格式】
    输出到文件 number.out 中。
    输出一个整数,表示答案对 1000000007 取模的值。

    发现2^1000经过一次计算就降到了1000一下,所以可以1000一下暴力搜索,然后再用组合数求值。
    记得特判下k=0和1的情况

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=10007;
    const ll mod=1000000007;
    int n,k,lim,num[N];
    ll ans,fac[N]={1};
    char ch[N];
    ll fp(ll x,ll k){
    	ll ans=1,s=x;
    	while(k){
    		if(k&1) ans=ans*s%mod;
    		k>>=1;
    		s=s*s%mod;
    	}
    	return ans;
    }
    inline ll inv(ll x){return fp(x,mod-2);}
    inline ll C(ll n,ll m){
    	if(n>m||n<0||m<0) return 0;
    	return fac[m]*inv(fac[n])%mod*inv(fac[m-n])%mod;
    }
    ll cal(int x){
    	ll res=0,cnt=0;
    	for(int i=0;i<n;++i)
    		if(num[i]==1){
    			res=(res+C(x-cnt,n-i-1))%mod;
    			++cnt;
    		}
    	if(cnt==x) res=(res+1)%mod;
    	return res;
    }
    void dfs(int onenum,int stp){
    	if(stp==k){
    		ans=(ans+cal(onenum))%mod;
    		return;
    	}
    	if(onenum>lim) return;
    	for(int i=max(fp(2,onenum)-1,2ll);i<=n;++i){
    		int cnt=0,temp=i;
    		while(temp){
    			if(temp&1) ++cnt;
    			temp>>=1;
    		}
    		if(cnt==onenum) dfs(i,stp+1);
    	}
    }
    int main()
    {
    	freopen("number.in","r",stdin);
    	freopen("number.out","w",stdout);
    	scanf("%s",ch);
    	n=strlen(ch);
    	scanf("%d",&k);
    	if(k==0){
    		printf("1\n");
    		return 0;
    	}
    	for(int i=0;i<n;++i) num[i]=ch[i]-'0';
    	lim=log(n)/log(2)+1;
    	for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
    	dfs(1,1);
    	if(k==1) --ans;
    	printf("%lld\n",ans);
        return 0;
    }
    

    T3

    【题目描述】
    S 市的有着非常特殊的城市规划,可以把它抽象成有 n 个点 n 条边的连通图。
    S 市的市长对这样的城市规划十分不满意,他想删去其中的一条路使得任意两点间
    有且仅有一条简单路径。
    S 市的市民对这样的城市规划也十分不满意,具体地,市民的不满意度为任意两点
    间最短距离的最大值。
    现在小 X 想知道,删去满足条件的一条道路后,市民不满意度的最小值。
    【输入格式】
    从文件 city.in 中读入数据。
    第一行一个正整数 n,表示城市的点数和边数。
    接下来 n 行,每行三个正整数 ui
    , vi
    ,wi,表示每条边的两个端点和长度。
    【输出格式】
    输出到文件 city.out 中。
    输出一个整数,表示答案

    考虑删掉一条边后,什么样的一条链会成为直径:
    把这个图看成是很多棵树由一个环相连:
    1.一棵树的直径就是这个图的直径。
    2.两个树最深的两个点之间的路径。
    删掉环上一条边显然不会影响第一种情况,考虑最小化第二种情况:
    假设环的长度为k,环上的点为a1,a2,...,ak,令a0=ak。
    令si表示ai到a1的距离。显然这是一个前缀和。
    以这个点为根的树上最深的点深度为dep1,dep2,…,depk。
    如果断开的路径为e(a{i-1},ai),分为三种情况:
    1.从1-i-1选两个点能取到最大值。
    2.从i-k选两个点取到最大值。
    3.前后各选一个点取到最大值。
    对于每条边,我们需要快速的求出三种情况的max。
    对于第一种情况,两点x,y间的距离为depx+depy+(sy-sx)=(depx-sx)+(depy+sy)
    对于第二种情况结果同第一种情况。
    对于第三种情况:设x<y,距离为depx+depy+(sk-(sy-sx))=sk+(depx+sx)+(depy-sy)。
    我们需要让三种情况的最大值最小。考虑求每种情况的最大值:
    预处理depi-si,depi+si,维护前缀和后缀最大值,就能得到每种情况的最大值,记录所有最大值的最小值,就是答案。

    反正我不会

    DAY2

    T1

    【题目描述】
    为缓解 S 城与日俱增的交通压力,S 城的市长准备修一条路。
    S 城共有 n 个街区,它们由 m 条双向道路相连,每条道路的长度相等。
    作为 S 城的天才,小 X 了解到 S 城的交通压力主要来自于最繁华的 S 街区和 T
    街区。如果新修的路不能使 S 街区到 T 街区的距离缩短,就不能缓解 S 城的交通压力。
    S 城的市长自然不了解这一点。现在小 X 想知道,有多少种修路方案不能缓解 S
    城的交通压力。
    注意,对于修路方案 (ui
    , ti) 和 (ti
    , ui) 视为同一种方案,新修的路不能在原图中存在。
    【输入格式】
    从文件 road.in 中读入数据。
    第一行四个整数 n, m, S, T。表示 S 城的街区数,道路数,繁华的两个街区的编号。
    接下来 m 行,每行两个数 ui
    , vi 表示一条道路上的两个街区。
    图中无重边和自环。
    【输出格式】
    输出到文件 road.out 中。
    输出一行一个整数。表示不能缓解交通压力的方案数。

    bfs求任意两点间的最短路,然后暴力枚举任意两点连边是否可以使最短路变短(类似dijsktra和floyed的松弛操作)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=2007;
    int n,m,S,T,tot,ans,head[N],dis[N][N];
    bool vis[N],edge[N][N];
    struct Edge{
    	int to,next;
    }e[N<<1];
    void add(int from,int to){
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    template<class T>inline void read(T &res){
    	static char ch;T flag=1;
    	while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
    	while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
    }
    queue<int> q;
    void bfs(int sta){
    	memset(vis,false,sizeof(vis));
    	q.push(sta);
    	vis[sta]=true;
    	while(q.size()){
    		int u=q.front();
    		q.pop();
    		for(int i=head[u];i;i=e[i].next){
    			int v=e[i].to;
    			if(vis[v]) continue;
    			vis[v]=true;
    			dis[sta][v]=dis[sta][u]+1;
    			q.push(v);
    		}
    	}
    }
    int main()
    {
    	freopen("road.in","r",stdin);
    	freopen("road.out","w",stdout);
    	read(n);read(m);read(S);read(T);
    	for(int i=1;i<=m;++i){
    		int u,v;
    		read(u);read(v);
    		add(u,v);add(v,u);
    		edge[u][v]=edge[v][u]=true;
    	}
    	for(int i=1;i<=n;++i) bfs(i);
    	for(int i=1;i<=n;++i)
    		for(int j=i+1;j<=n;++j){
    			if(edge[i][j]) continue;
    			if(dis[S][T]>dis[S][i]+dis[j][T]+1||dis[S][T]>dis[S][j]+dis[i][T]+1){
    				++ans;
    //				printf("%d %d\n",i,j);
    			}
    		}
    	printf("%d\n",n*(n-1)/2-m-ans);
        return 0;
    }
    

    T2

    【题目描述】
    everlasting 有 n 个神奇的集合,编号为 1n。开始时它们都是空的,现在 everlasting
    要对它们进行两种操作:

    1. 将元素 x 加入编号 [l,r] 的集合中。神奇的是,如果一个集合原本就有 x,那么
      该集合中所有元素的个数都会翻倍
    2. 询问编号 [l,r] 的集合元素个数的和,对 998244353 取模。
      everlasting 当然不会做啦,但是他想考考你...
      【输入格式】
      从文件 multiset.in 中读入数据。
      第一行两个正整数 n, q, 表示集合个数和询问数量。
      接下来 q 行,首先是一个整数 opt:
      若 opt = 1,接下来三个整数 l,r, x,表示向编号 [l,r] 的集合中加入 x。
      若 opt = 2,接下来两个整数 l,r,表示询问编号 [l,r] 的集合的元素个数和。
      【输出格式】
      输出到文件 multiset.out 中。
      对于每个询问,输出一行一个整数,表示答案。

    显然可以开n个线段树来维护n个元素的覆盖情况,动态开点防止空间爆炸。剩下就很简单了。

    考场调了2.5h也没打出来,旁边的myg大佬一下就打完了,再此膜拜。

    #include<cstdio>
    using namespace std;
    #define ll long long
    const ll mod=998244353;
    const int N=8000007;
    int n,q,ndnum,root,a[N>>5],ls[N],rs[N];
    ll sum[N],fla[N],flm[N];
    template<class T>inline void read(T &res){
    	static char ch;T flag=1;
    	while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
    	while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
    }
    int build(int l,int r){
    	int p=++ndnum;
    	if(l==r){
    		sum[p]=0;
    		flm[p]=1;
    		return p;
    	}
    	int mid=(l+r)>>1;
    	ls[p]=build(l,mid);
    	rs[p]=build(mid+1,r);
    	sum[p]=sum[ls[p]]+sum[rs[p]];
    	flm[p]=1;
    	return p;
    }
    void pushdown(int p,int l,int r){
    	int mid=(l+r)>>1;
    	sum[ls[p]]=(sum[ls[p]]*flm[p]%mod+(mid-l+1)*fla[p]%mod)%mod;
    	sum[rs[p]]=(sum[rs[p]]*flm[p]%mod+(r-mid)*fla[p]%mod)%mod;
    	fla[ls[p]]=(fla[ls[p]]*flm[p]%mod+fla[p])%mod;
    	fla[rs[p]]=(fla[rs[p]]*flm[p]%mod+fla[p])%mod;
    	flm[ls[p]]=flm[ls[p]]*flm[p]%mod;
    	flm[rs[p]]=flm[rs[p]]*flm[p]%mod;
    	fla[p]=0;flm[p]=1;
    }
    int xl,xr,yl,yr,x;
    ll query(int l,int r,int p){
    	if(xl<=l&&r<=xr) return sum[p];
    	pushdown(p,l,r);
    	int mid=(l+r)>>1;
    	ll res=0;
    	if(xl<=mid) res+=query(l,mid,ls[p]);
    	if(xr>mid) res+=query(mid+1,r,rs[p]);
    	return res%mod;
    }
    int sett(int l,int r,int p){
    	if(p==0) p=++ndnum;
    	if(yl<=l&&r<=yr){
    		sum[p]=1;
    		fla[p]=true;
    		return p;
    	}
    	int mid=(l+r)>>1;
    	if(yl<=mid){
    		if(!ls[p]) ls[p]=++ndnum;
    		ls[p]=sett(l,mid,ls[p]);
    	}
    	if(yr>mid){
    		if(!rs[p]) rs[p]=++ndnum;
    		rs[p]=sett(mid+1,r,rs[p]);
    	}
    	if(sum[ls[p]]==sum[rs[p]]) sum[p]=sum[ls[p]];
    	else sum[p]=-1;
    	return p;
    }
    int _get(int l,int r,int p){
    	if(yl<=l&&r<=yr) return sum[p];
     	int mid=(l+r)>>1;
    	int la=100,ra=100;
    	if(yl<=mid){
    		if(!ls[p]) ls[p]=++ndnum;
    		if(fla[p]==1) sum[ls[p]]=fla[ls[p]]=1;
    		la=_get(l,mid,ls[p]);
    	}
    	if(yr>mid){
    		if(!rs[p]) rs[p]=++ndnum;
    		if(fla[p]==1) sum[rs[p]]=fla[rs[p]]=1;
    		ra=_get(mid+1,r,rs[p]);
    	}
    	if(la==100) return ra;
    	if(ra==100) return la;
    	if(la==ra) return la;
    	return -1;
    }
    void modify(int l,int r,int p,int c){
    	if(xl<=l&&r<=xr){
    		if(c==1){
    			sum[p]=(sum[p]+r-l+1)%mod;
    			fla[p]=(fla[p]+1)%mod;
    			return;
    		}
    		if(c==2){
    			sum[p]=sum[p]*2%mod;
    			fla[p]=fla[p]*2%mod;
    			flm[p]=flm[p]*2%mod;
    			return;
    		}
    		yl=l,yr=r;
    		int y=_get(1,n,x);
    		if(y==0) c=1;
    		else if(y==1) c=2;
    		else c=0;
    		if(c==1){
    			sum[p]=(sum[p]+r-l+1)%mod;
    			fla[p]=(fla[p]+1)%mod;
    			return;
    		}
    		if(c==2){
    			sum[p]=sum[p]*2%mod;
    			fla[p]=fla[p]*2%mod;
    			flm[p]=flm[p]*2%mod;
    			return;
    		}
    	}
    	pushdown(p,l,r);
    	int mid=(l+r)>>1;
    	if(xl<=mid) modify(l,mid,ls[p],c);
    	if(xr>mid) modify(mid+1,r,rs[p],c);
    	sum[p]=(sum[ls[p]]+sum[rs[p]])%mod;
    }
    int main()
    {
    	freopen("multiset.in","r",stdin);
    	freopen("multiset.out","w",stdout);
    	read(n);read(q);
    	ndnum=n;
    	root=build(1,n);
    	while(q--){
    		int opt;
    		read(opt);
    		if(opt==1){
    			read(xl);read(xr);read(x);
    			modify(1,n,root,0);
    			yl=xl;yr=xr;
    			sett(1,n,x);
    		}
    		else if(opt==2){
    			read(xl);read(xr);
    			printf("%lld\n",query(1,n,root));
    		}
    	}
    //	while(q--){
    //		int opt;
    //		read(opt);
    //		if(opt==1){
    //			read(yl);read(yr);read(x);
    //			sett(1,n,x);
    //		}
    //		else if(opt==2){
    //			read(yl);read(yr);read(x);
    //			printf("%d\n",_get(1,n,x));
    //		}
    //	}
        return 0;
    }
    

    T3

    【题目描述】
    小 X 同学觉得树上问题太毒瘤了,于是决定将树上的边删去,最终变成一个点。
    现在有一个 n 个节点的树,他的游戏是这样的:

    1. 从剩下的所有边中等概率随机选中一条边 T。
    2. 将这条边删去,若这条边相连的两个点编号为 u 和 v,新建一个点 x,这个点与
      所有与 u 和 v 相邻的点有边,最后删去 u 和 v 及与它们相连的边,x 的编号等概率随
      机命名为 u 或 v。
      不断重复上述步骤,知道只剩下一个点。
      现在小 X 想知道,对于每个编号,最后剩下该编号的点的概率。
      树是一个没有环的连通图。
      【输入格式】
      从文件 tree.in 中读入数据。
      第一行一个整数 n。
      接下来 n 1 行,第 i 行两个整数 ui
      , vi,表示第 i 条边连接的两个点。
  • 相关阅读:
    关于拉格朗日乘子法和KKT条件
    深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件
    安全模式下运行Windows installer并卸载程序
    win10 进入安全模式的方法
    支持向量机通俗导论(理解SVM的三层境界)
    Windows系统防火墙用法
    浏览器起始页被篡改恶意跳转解决方法
    网络熟知端口号
    SSL/TLS协议运行机制的概述
    分布式拒绝服务攻击 DDoS
  • 原文地址:https://www.cnblogs.com/hzyhome/p/11658224.html
Copyright © 2011-2022 走看看