CF1523

A:

分析题意后,可以得到4种情况

  1. 字符串位置本来就是1,不变
  2. 本身是0,但两边1的距离相等,不变成1.
  3. 本身是0,左右两边1距离小于操作次数,就输出1。
  4. 其余情况都为0

这样我们就可以前缀和后缀进行计算,同时处理边界情况,就可以解决问题了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int t,n,m;
int b[N],p[N];
int inf=0x3f3f3f3f;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		string a;
		cin>>a;
		b[0]=-inf;
		p[n+1]=inf;
		string x="0";
		x+=a;
		for(register int i=1;i<=n;++i){
			if(x[i]=='1') b[i]=i;
			else b[i]=b[i-1];
		}
		for(register int i=n;i>=1;--i){
			if(x[i]=='1') p[i]=i;
			else p[i]=p[i+1];
		}
		for(register int i=1;i<=n;++i){
			if(x[i]=='1') cout<<x[i];
			else{
				int l=i-b[i],r=p[i]-i;
				if(l==r) printf("%d",0);
				else if(l<=m or r<=m) printf("%d",1);
				else printf("%d",0);
			}
		}
		printf("\n");
	}
	return 0;
}

B:

分析一下两个点之间的关系:

这样就不用考虑具体情况了。

我们设\(a_i,a_j\)\(i<j\)

  1. 执行操作2,变为:

\[a_i,a_j-a_i \]

  1. 执行操作1,变为:

\[a_j,a_j-a_i \]

  1. 执行操作2,变为:

\[a_j,-a_i \]

  1. 执行操作1,变为:

\[a_j-a_i,-a_i \]

  1. 执行操作2,变为:

\[a_j-a_i,-a_j \]

  1. 执行操作1,变为:

\[-a_i,-a_j \]

我们不需要考虑数值,直接这样输出即可。

#include<bits/stdc++.h>
using namespace std;
int T,n;
signed main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=0,x;i<n;i++) scanf("%d",&x);
		printf("%d\n",n*3);
		for(int i=1;i<=n;i+=2){
			printf("1 %d %d\n",i,i+1);
			printf("2 %d %d\n",i,i+1);
			printf("1 %d %d\n",i,i+1);
			printf("1 %d %d\n",i,i+1);
			printf("2 %d %d\n",i,i+1);
			printf("1 %d %d\n",i,i+1);
		}
	}
	return 0;
}

C:

考虑某一次操作最后面是 \(x\)

如果 \(x=1\),那么上一次操作只可以是在结尾加了一个 \(1\)
如果 \(x>1\),那么上一次只能进行删除末尾若干个数,然后让末尾数 \(+1\),那么可以确定末尾数位 \(x-1\).
我们知道这个之后我们就进行构造即可:

\(x=1\) 直接结尾加 \(1\) 即可
剩下的情况我们直接往前找,找到数为 \(x-1\) 的地方将 \(x-1\) 变成 \(x\) 即可.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int T,n,a[N],b[N],cnt;
void solve(){
    cnt=0;
    cin >> n;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=n;i++){
        if(a[i]==1){
            b[++cnt]=1;
        }else {
            while(1){
                if(b[cnt]==a[i]-1){
                    b[cnt]=a[i];
                    break;
                }else {
                    cnt--;
                }
            }
        }
        for(int i=1;i<=cnt;i++){
            if(i!=cnt) cout<<b[i]<<".";
            else cout<<b[i]<<endl;
        }
    }
}
signed main(){
    cin>>T;
    while(T--){
        solve();
    }
    // system("pause");
    return 0;
}

D:

我不理解,但我大受震撼,这题竟然用随机数解决。

这道题的题意很清楚,就是我们熟悉的一个模型,

可是这题的范围又无法用直接经典的状压做,

所以此题的突破口就在于人数不小于\(\lceil \frac{n}{2} \rceil\)

我们知道了其中的一个人,那么就可以只看那个人所喜欢的不超过15种货币。用状压即可解决。

我们怎么找到任意一个呢?

随机抽50次,概率巨小,可以忽略错误的可能性。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200005
#define uns unsigned
#define INF 0x3f3f3f3f
#define MOD 1000000007ll
#define lowbit(x) ((x)&(-(x)))
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,m,p,b[MAXN],c[MAXN],bt[20],ns[MAXN];
ll a[MAXN];
int main()
{
	srand(time(0));
	ns[0]=0,ns[1]=1;
	for(int i=2;i<MAXN-4;i++)ns[i]=ns[i>>1]+(i&1);
	n=read(),m=read(),p=read();
	for(int i=1;i<=n;i++){
		char c;
		for(int j=1;j<=m;j++){
			c=getchar();
			while(c!='0'&&c!='1'&&c>0)c=getchar();
			a[i]=(a[i]<<1)+c-'0';
		}
	}
	ll PT=0;int NS=0;
	for(int K=50;K--;){
		int x=1ll*rand()*rand()%n+1,tot=0;//随机选择观众
		for(int j=1;j<=m;j++) if((a[x]>>(j-1))&1)bt[++tot]=j;
		memset(c,0,sizeof(c));
		for(int i=1;i<=n;i++){
			b[i]=0;
			for(int j=1;j<=tot;j++) if((a[i]>>(bt[j]-1))&1)b[i]|=(1<<(j-1)); c[b[i]]++;
		}
		for(int k=tot;k>0;k--)
			for(int i=(1<<tot)-1;i>=0;i--)
				if((i>>(k-1))&1)c[i-(1<<(k-1))]+=c[i];
		int ans=0;
		for(int i=1;i<(1<<tot);i++) if(c[i]>=((n+1)>>1)&&ns[i]>ns[ans])ans=i;
		if(ans>0){
			ll pt=0;
			for(int i=1;i<=tot;i++)
				if((ans>>(i-1))&1)pt|=(1ll<<(bt[i]-1));
			if(ns[ans]>NS)PT=pt,NS=ns[ans];
		}
	}
	for(int i=m;i>0;i--)putchar(((PT>>(i-1))&1)+'0');
	puts("");
    // system("pause");
	return 0;
}

E:

期望题目

\(n\) 个台灯初始时都是暗的,每次等概率随机一个暗台灯将其点亮,若点亮后存在一个长度为 \(k\) 的连续段有大于一个台灯被点亮则立刻停止,求期望点亮多少台灯。答案对 \(10^9+7\) 取模。

我们记 \(f_i\) 表示选择 \(i\) 次的期望,则有:

\[ans==\sum^{n}_{i=1}f_i\times i=\sum^{n}_{i=1}\sum^{n}_{j=i}f_i \]

考虑如何求出:\(g_i=\sum^{n}_{j=i+1}f_i\),也就是 至少选择 \(i+1\) 次后才停止的期望。

显然 \(g_i\) 等价于选择了 \(i\) 次依然没有停止的期望。那么问题转化为有 \(n\) 个灯,选择其中 \(i\) 个,两两之间距离大于等于 \(k−1\) 的期望。

我们可以把每一个灯看做一个长度为 \(k\) 的线段,那么就是选择 \(i\) 个长度为 \(k\) 的区间互相不交。注意最后一个区间的右端点是可以超过 \(n\) 的。
那么我们直接把每一个区间的后 \(k−1\) 个元素删掉,问题转化为在 \(n−(i−1)(k−1)\) 个灯中选择 \(i\) 个的方案数。这个东西和刚刚的模型是等价的。假设现在的模型中选择了 \(i,j(i<j)\) 为相邻两盏灯,那么原模型中这两盏灯之间的距离就是 \(j−i+k−1\)
所以有

\[g_i=\frac{\binom{n-(i-1)(k-1)}{i}}{\binom{n}{i}} \]

答案就是:\(\sum^{n-1}_{i=0}g_i\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=1e9+7;
int Q,n,k,ans,fac[N],inv[N];
ll qmi(ll x,ll k){
	ll ans=1;
	for (;k;k>>=1,x=x*x%mod)
		if (k&1) ans=ans*x%mod;
	return ans;
}
ll C(ll n,int m){
	if (n<m) return 0;
	return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll invC(int n,int m){
	if (n<m) return 0;
	return 1LL*inv[n]*fac[m]%mod*fac[n-m]%mod;
}
int main(){
	fac[0]=inv[0]=1;
	for (int i=1;i<N;i++) fac[i]=1LL*fac[i-1]*i%mod;
	inv[N-1]=qmi(fac[N-1],mod-2);
	for (int i=N-2;i>=1;i--) inv[i]=1LL*inv[i+1]*(i+1)%mod;
	scanf("%d",&Q);
	while (Q--){
		scanf("%d%d",&n,&k);
		ans=1;
		for (int i=1;i<n;i++)
			ans=(ans+C(n-1LL*(i-1)*(k-1),i)*invC(n,i))%mod;
		cout<<ans<<endl;
	}
	return 0;
}

F:

看数据范围就是 \(dp\)

在移动过程中,有用的位置显然就只有塔的位置和任务的位置,对于任务只需要知道当前任务对应的时间它在不在这个位置,所以有用的时间只有任务对应的时间和经过塔的时间。

由于要让答案最大,如果当前确定了遍历过哪些塔,且最后一步是在塔中的话(不是在塔中的话就是刚完成了一个任务,即上面的 \(f\) )。

我们接下去的目标:

  1. 走到一座新的塔中,

  2. 走到一个任务,要完成某个任务,且前面塔的状态已知了,只需要知道能不能在合法时间内走到这个任务即可,如果时间越小肯定越优

#include<bits/stdc++.h>
using namespace std;
const int N=16500, M=105, Inf=1e9+5;
struct node{
	int x, y, t;
}a[18], b[M];
int n, m, nn, ans, dit[N][18], dis[N][M], f[N][M], g[N][M];
inline int read(){
	char c=getchar();
	int ans=0;
	while (c<48 || c > 57) c=getchar();
	while (c >= 48 && c<=57) ans=(ans<<3)+(ans<<1)+(c ^ 48), c=getchar();
	return ans;
}

inline bool cmp(node x, node y){
	return x.t<y.t;
}
int main()
{
	n=read(), m=read(), nn=1<<n;
	for (int i=0; i<n;++i) a[i].x=read(), a[i].y=read();
	for (int i=1; i<=m;++i) b[i].x=read(), b[i].y=read(), b[i].t=read();
	sort(b+1, b+m+1, cmp);
	for (int i=0; i<nn;++i)
		for (int j=0; j<=m;++j) g[i][j]=Inf;
	for (int i=0; i<nn;++i){
		int u;
		for (int j=0; j<n;++j)
			if ((i >> j) & 1){
				u=j;
				for (int k=0; k<=m;++k) g[i][k]=min(g[i][k], g[i ^ (1<<j)][k]+dit[i ^ (1<<j)][j]);
			}
		if (!i){
			g[0][0]=0;
			for (int j=0; j<n;++j) dit[0][j]=Inf;
			for (int j=1; j<=m;++j) dis[0][j]=Inf, f[0][j]=1;
		}
		else if (i==(1<<u)){
			g[i][0]=0;
			for (int j=0; j<n;++j) dit[i][j]=abs(a[u].x-a[j].x)+abs(a[u].y-a[j].y);
			for (int j=1; j<=m;++j) dis[i][j]=abs(a[u].x-b[j].x)+abs(a[u].y-b[j].y);
		}
		else{
			int v=i ^ (1<<u);
			for (int j=0; j<n;++j) dit[i][j]=min(dit[v][j], dit[1<<u][j]);
			for (int j=1; j<=m;++j) dis[i][j]=min(dis[v][j], dis[1<<u][j]);
		}
		for (int j=1; j<=m;++j){
			for (int k=j-1; k >= f[i][j]; --k)
				if (g[i][k]+dis[i][j]<=b[j].t) f[i][j]=k+1;
			if (!f[i][j]) continue;
			for (int k=j+1; k<=m;++k)
				if (b[j].t+min(dis[i][k], abs(b[k].x-b[j].x)+abs(b[k].y-b[j].y))<=b[k].t) f[i][k]=max(f[i][k], f[i][j]+1);
			g[i][f[i][j]]=min(g[i][f[i][j]], b[j].t), ans=max(ans, f[i][j]);
			for (int k=0; k<n;++k)
				if (!((i >> k) & 1)) g[i|(1<<k)][f[i][j]]=min(g[i|(1<<k)][f[i][j]], b[j].t+min(dit[i][k], abs(a[k].x-b[j].x)+abs(a[k].y-b[j].y)));
		}
	}
	printf("%d\n", ans);
    // system("pause");
	return 0;
}