B 思维
需要靠灵机一动的一道思维,不太聪明的本思维苦手还在一个个凑…
发现性质:一张图雷和空地逆转后,空地的数字不变,因为相邻雷和空地是两两一对
又发现从B图变到A图和A的逆转图的操作次数和是n*m
const int N=1e3+10,mod=1e9+9;
int n,m;
string a[N],b[N],ia[N];
map<
char,char>cg={
{
'.','X'
},{
'X','.'
}
};
void printmap(string s[]){
forr(i,0,n-1){
cout<<s[i]<<endl;
}
}
void solve(){
//B: 结果相同B->A
//发现神奇性质 A图全反转后和原来的图结果一样 B到A/inv(A)的修改ans1+ans2=n*m 中总有一个相差<nm/2的
cin>>n>>m;
forr(i,0,n-1)cin>>a[i];
forr(i,0,n-1)cin>>b[i];
forr(i,0,n-1){
forr(j,0,m-1)ia[i]+=cg[a[i][j]];
}
int ans1,ans2;
ans1=ans2=0;
forr(i,0,n-1){
forr(j,0,m-1){
ans1+=(a[i][j]!=b[i][j]);
ans2+=(ia[i][j]!=b[i][j]);
}
}
if(ans1<ans2){
printmap(a);
}else printmap(ia);
}
M DFS
发现文件夹是一个树状结构,把不用ignore的路径都标记,说明不能一股脑git文件夹,还得下一层找。
const int N=1e3+10,mod=1e9+9;
int n,m;
unordered_map<string,set<string>>tr;
unordered_map<string,int>st;
void op(string s,int fg){
//处理路径
string pre="A",now;
char tag='0';
//层数标记 防止路径上的文件夹重名
for(auto x:s){
if(x=='/'){
st[pre]=fg;
now+=(tag++);
tr[pre].insert(now);
pre=now;
}
now+=x;
}
string nowf='F'+now+tag;
//文件打上标记和路径上的文件夹区分
st[pre]=st[nowf]=fg;
tr[pre].insert(nowf);
}
int dfs(string now){
// cout<<now<<' '<<st[now]<<endl;
if(st[now]==0)return 1;
//只需要一次查找
else if(now[0]=='F')return 0;
//到文件了
int ret=0;
for(auto x:tr[now]){
int xret=dfs(x);
ret+=xret;
}
return ret;
}
void solve(){
//G
tr.clear();
st.clear();
cin>>n>>m;
forr(i,1,n){
string ig;
cin>>ig;
op(ig,0);
}
forr(i,1,m){
string nig;
cin>>nig;
op(nig,1);
}
st["A"]=1;
//根 必须向下遍历
cout<<
dfs("A")<<endl;
}
D 三分 贪心
double n,p1,v1,p2,v2;
/* 废片 没用三分 两点向两边走再向中间汇总
double ans1(){
double ans=0;
double t1=p1/v1,t2=(n-p2)/v2;
ans+=max(t1,t2);
if(t1<t2){
double dt=t2-t1,tp1=dt*v1;
ans+=(n-dt)/(v1+v2);
}else{
double dt=t1-t2,tp2=n-dt*v2;
ans+=tp2/(v1+v2);
}
return ans;
}
*/
//三分中间汇总的距离 取到最小时间
double ans2(){
double ans=1e18;
double l=p1,r=p2;
auto cal=[](double x)->
double{
double t1=(x+min(p1,x-p1))/v1,t2=(n-x+min(p2-x,n-p2))/v2;
return max(t1,t2);
};
while (r-l>
1e-10)
{
double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
double tm1=cal(mid1),tm2=cal(mid2);
ans=min({ans,tm1,tm2
});
if(tm1<tm2){
r=mid2;
}else{
l=mid1;
}
}
return ans;
}
//特殊情况
double ans3(){
double a3=max((n-p1)/v1,p2/v2);
//对冲
//一个人走
double a1=(min(p1,n-p1)+n)/v1;
double a2=(min(p2,n-p2)+n)/v2;
return min({a1,a2,a3
});
}
void solve(){
cin>>n>>p1>>v1>>p2>>v2;
cout<<fixed<<
setprecision(10);
/* 这里wa13 两人同位置不一定向两边走,也可能一人走得飞快把路都走完
if(p1==p2){
cout<<n/(v1+v2)<<endl;
return;
}
*/
if(p1>p2){
swap(p1,p2);
swap(v1,v2);
}
// cout<<ans1()<<' '<<ans2()<<' '<<ans3()<<endl;
cout<<
min({
ans2(),ans3()
})<<endl;
}
I dp
O(1)做法好烧脑,只学习了O(n)做法,对我来说也很难想到…
dalao题解
const double pi=acos(-1);
void solve(){
int n,m;cin>>n>>m;
//O(n)做法
//每个点到其他点的距离都一样 只求一个点就可以 分同层和异层去求
vector<
double>
dp(n+1,0),g(n+1,0);
//dp[i] 外层一点 到其他所有点距离
forr(i,1,m-1){
g[1]+=min(2.0,i*pi/(m*1.0));
//一个点到同层其他点
}
g[1]*=2;
//对称
g[1]+=2;
//对称轴上的直边
dp[1]=g[1];
forr(i,2,n){
g[i]=i*g[1];
dp[i]=dp[i-1]+g[i]+(i-1)*2.0*m;
//dp[i-1]:异层 g[i]同层 (i-1)*2m到第一层的直边:两图中发现想要换到其他角度,走最内层或内层直边最近
}
double ans=0;
forr(i,1,n){
//异层 + 同层(两两组合会重复,去掉一个g[i]) + 到中心
ans+=2.0*m*(dp[i]-g[i])+1.0*m*g[i]+(m==1?0:i*2.0*m);
//m=1时 g[1]中已经算过每个点到中心的边
}
cout<<fixed<<
setprecision(8)<<ans<<endl;
}
C 二进制数位dp
dalao题解
分析原式子
∑
i
=
0
X
∑
j
=
[
i
=
0
]
Y
[
i
&
j
=
0
]
⌊
log
2
(
i
+
j
)
+
1
⌋
\sum_{i=0}^{X}\sum_{j=[i=0]}^{Y}[i\&j=0]\lfloor\log_2(i+j)+1\rfloor
∑i=0X∑j=[i=0]Y[i&j=0]⌊log2(i+j)+1⌋
- i & j = 0 i\&j=0 i&j=0,i j每一位相与=0才有贡献
- ⌊ log 2 ( i + j ) + 1 ⌋ \lfloor\log_2(i+j)+1\rfloor ⌊log2(i+j)+1⌋是(i+j)的二进制最高1的位置
枚举i,j的每一位二进制,计算符合 i & j = 0 i\&j=0 i&j=0的(i,j)个数
const int N=100,mod=1e9+7,inf=1e9+10;
int a[N],b[N],dp[N][2][2];
//二进制数位dp
int dfs(int pos,int limit1,int limit2){
if(pos==0)return 1;
if(~dp[pos][limit1][limit2])return dp[pos][limit1][limit2];
int up1=(limit1?a[pos]:1);
int up2=(limit2?b[pos]:1);
int res=0;
forr(i,0,up1){
forr(j,0,up2){
if((i&j)==0){
res+=dfs(pos-1,limit1&&up1==i,limit2&&up2==j);
res%=mod;
}
}
}
return dp[pos][limit1][limit2]=res;
}
void solve(){
memset(a,0,sizeof a);
memset(b,0,sizeof b);
memset(dp,-1,sizeof dp);
int x,y;cin>>x>>y;
int cnt1,cnt2;
cnt1=cnt2=0;
while (x)
{
a[++cnt1]=x%2;
x/=2;
}
while (y)
{
b[++cnt2]=y%2;
y/=2;
}
int len=max(cnt1,cnt2),ans=0;
forr(l,1,len){
//枚举位数
int cnt=0;
//答案种数计数
//最高位为L 意味着i或j有一个数这一位是1
//但是i&j=0只有一个数高位是1
if(l<=cnt1){
//i的高位1
cnt+=dfs(l-1,l==cnt1,l>cnt2);
}
if(l<=cnt2){
//j的高位是1
cnt+=dfs(l-1,l>cnt1,l==cnt2);
}
ans=(ans+cnt*l%mod)%mod;
//计算贡献
}
cout<<ans<<endl;
}