A. Rainbow的信号
很巧妙,适宜反复赏玩
code :
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
int n,last[3],a[maxn],b[maxn];
double ansxor,ansand,ansor;
void Rainbow(int k)
{
	last[0]=last[1]=0;
	int c1=0,c2=0;
	double w=(1<<k)*1.0/n/n;//最后写成/(n*n)就爆int了
	for(int i=1;i<=n;i++) {
		b[i]=(a[i]>>k)&1;
		if(b[i])/*区间长度为1即l=r=i*/ {
			ansxor+=w;
			ansand+=w;
			ansor+=w;
		}
		//以下计算长度大于等于2的区间 
		if(!b[i]) ansor+=2*w*last[1];
		else {
			ansand+=2*w*(i-1-last[0]); //减1是因为区间[r,r]已经被计算过了 
			ansor+=2*w*(i-1);
		} 
		ansxor+=2*w*(b[i]?c1:c2);
		c1++;
		if(b[i]) swap(c1,c2);
		last[b[i]]=i;
	}
} 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=0;i<31;i++) Rainbow(i);
	printf("%.3lf %.3lf %.3lf",ansxor,ansand,ansor);
	return 0;
}


B. 绿豆蛙的归宿
算期望,倒推计算,建反图的思想
code :
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 3;
int tot,ver[maxn],head[maxn],Next[maxn],edge[maxn],deg[maxn],out[maxn];
double d[maxn];
void add(int x,int y,int z)
{
	ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void bfs(int s)
{
	queue<int> q;
	q.push(s);
	while(!q.empty())
	{
		int x=q.front(),y,z;
		q.pop();
		for(int i=head[x];i;i=Next[i]) {
			y=ver[i],z=edge[i];
			d[y]+=(d[x]+z)/out[y];
			deg[y]--;
			if(!deg[y]) q.push(y);
		}	
	}
}
int main()
{
	int n,m,a,b,c;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {
		scanf("%d%d%d",&a,&b,&c);
		deg[a]++; out[a]++;//反图中的入度,原图中的出度,两者等价 
		add(b,a,c);//建原图的反图 
	} 
	bfs(n);//从n倒着算到1 
	printf("%.2lf",d[1]);
	return 0;
}


C. 扑克牌

code :