标签:排队 sqrt second ons std 一个 节点 top 规律
中位线定理可得,设三角形 \(DEF\) 对边的中点分别位 \(ABC\) ,则 \(\vec{BC}={1\over 2}\vec{FE}=\vec{FA}=\vec{AE}\)
因此,考虑求解 \(E\) 点坐标等价于求解 \(\vec{OE}\) 。则 \(\vec{OE}=\vec{OA}+\vec{AE}=\vec{OA}+\vec{OC}-\vec{OB}\)
不难知道,三角形两中点和减去第三个中点即可得到第三个中点对角的坐标。按这个方法写完排个序即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
pii operator + (const pii &a,const pii &b) { return pii(a.first+b.first,a.second+b.second); }
pii operator - (const pii &a,const pii &b) { return pii(a.first-b.first,a.second-b.second); }
int main(){
pii Pnt[5];
for(int i=1;i<=3;i++) cin>>Pnt[i].first>>Pnt[i].second;
pii Ans[5];
Ans[1]=Pnt[1]+Pnt[2]-Pnt[3];
Ans[2]=Pnt[1]+Pnt[3]-Pnt[2];
Ans[3]=Pnt[2]+Pnt[3]-Pnt[1];
sort(Ans+1,Ans+4);
for(int i=1;i<=3;i++)
cout<<Ans[i].first<<" "<<Ans[i].second<<endl;
return 0;
}
不难想出,要使得选出的 \((x,y)\) 奇偶性不同,则必然是 \(x\) 与 \(y\) 一个为奇数,一个为偶数
由于 \(n\) 中的奇数个数为 \(\lfloor{n+1\over 2}\rfloor\) ,偶数个数为 \(\lfloor{n\over 2}\rfloor\)
通过暴力找规律(写一个代码,算不同的 \(n,m\) 时,\((\lfloor{n+1\over 2}\rfloor\cdot \lfloor{m\over 2}\rfloor+\lfloor{n\over 2}\rfloor\cdot \lfloor{m+1\over 2}\rfloor)\) )亦或者稍微证明一下,不难发现,每个 \(n,m\) 对答案的贡献为 \(\lfloor{nm\over 2}\rfloor={nm-(n\&1)\cdot(m\&1)\over 2}\)
现只需考虑如何实现即可:由于 \(nm\) 的乘积在取模意义下等于两者各自的取模,再乘积的取模。\((n\&1)\cdot (m\&1)\) 仅与最后一位有关
故将串 \(n,m\) 前补足 \(0\) ,使得两者长度相同。然后从前往后,一边记录到当前位置 \(n,m\) 在取模意义下的值,一边计算对答案的贡献:若当前二进制位 \(n,m\) 均为 \(1\) ,则对答案的贡献为 \((nm-1)\mod p\) ,否则为 \(nm\mod p\) 。求和即可算出答案。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7,inv2=MOD+1>>1,MAXN=1e6+10;;
stack<int> num[3];
string s;
inline ll ans(){
for(int i=1;i<=2;i++){
getline(cin,s);
for(int j=s.size()-1;j>=0;j--)
num[i].push(s[j]==‘1‘);
}
while(num[1].size()<num[2].size()) num[1].push(0);
while(num[1].size()>num[2].size()) num[2].push(0);
ll A=0,B=0,Ans=0;
for(int i=num[1].size();i>0;i--){
int a=num[1].top(); num[1].pop();
int b=num[2].top(); num[2].pop();
A=((A<<1)|a)%MOD;
B=((B<<1)|b)%MOD;
ll Tmp=A*B-(a&b);
Tmp=Tmp%MOD*inv2%MOD;
Ans=(Ans+Tmp)%MOD;
}
return Ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout<<ans()<<endl;
return 0;
}
这题本人做法略微有些复杂,使用了分块+双端队列
将每 \(\sqrt n\) 只熊猫装在一个块内,每只熊猫维护一下当前它所在的块。当熊猫 \(i\) 走到队首时,我们可以对于熊猫 \(i\) 所在的块内暴力删除这个熊猫,再把它加到开头那个分块内。接着,从它原先在的块开始,把前一个块的最后一个弹出,扔进当前块内。记得在处理时更新一下被改动的熊猫所在的块。走到队尾也是类似的处理。
由于原先所在块内有 \(\sqrt n\) 只熊猫,暴力删除的复杂度是 \(O(\sqrt n)\) 的;对外每块末尾扔进后一个块,单次处理是 \(O(1)\) 的,总的次数是 \(O(\sqrt n)\) 次。因此修改的复杂度是 \(O(\sqrt n)\) 的。
查询当前第 \(i\) 只熊猫,可先从分块外扫过去,直到扫到查询熊猫所在分块;在进入分块暴力查询;也可直接用公式算出在第几个分块,然后进入暴力查询。
外面扫分块的复杂度为 \(O(\sqrt n)\) ,用公式直接算出在哪个分块是 \(O(1)\) 的;分块内暴力查询的复杂度是 \(O(\sqrt n)\) 的。因此查询的复杂度是 \(O(\sqrt n)\) 的。
因此,总复杂度为 \(O(q\sqrt n)\) 。过不了就卡卡常数。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
typedef deque<int> dq;
int N,Q;
struct SQRT{
dq DQ;
inline void insertBack(int val) { DQ.push_back(val); }
inline void insertFront(int val) { DQ.push_front(val); }
inline int popBack(){
int res=DQ.back();
DQ.pop_back();
return res;
}
inline int popFront(){
int res=DQ.front();
DQ.pop_front();
return res;
}
inline void popElem(int val){
dq::iterator p=DQ.begin();
while(*p!=val) p++;
DQ.erase(p);
}
inline int ans(int pos){
for(auto e : DQ)
if(pos) pos--;
else return e;
}
};
struct MAIN{
int Siz;
int Cnt;
int Set[MAXN];
SQRT S[500];
inline void Front(int val){
int pos=Set[val],res;
S[pos].popElem(val);
for(int i=pos;i>0;i--){
res=S[i-1].popBack();
Set[res]=i;
S[i].insertFront(res);
}
S[0].insertFront(val);
Set[val]=0;
}
inline void Back(int val){
int pos=Set[val],res;
S[pos].popElem(val);
for(int i=pos;i<Cnt;i++){
int res=S[i+1].popFront();
Set[res]=i;
S[i].insertBack(res);
}
S[Cnt].insertBack(val);
Set[val]=Cnt;
}
inline int ans(int pos){
int Tot=0;
for(int i=0;i<=Cnt;i++)
if(pos>=Tot&&pos<Tot+Siz) return S[i].ans(pos-Tot)+1;
else Tot+=Siz;
}
}M;
inline void pre(){
cin>>N>>Q;
M.Siz=floor(sqrt(N));
M.Cnt=(N-1)/M.Siz;
for(int i=0;i<N;i++)
M.S[i/M.Siz].insertBack(i),M.Set[i]=i/M.Siz;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
pre();
int T,I;
while(Q--&&cin>>T>>I)
if(0);
else if(T==1) M.Front(I-1);
else if(T==2) M.Back(I-1);
else if(T==3) cout<<M.ans(I-1)<<endl;
return 0;
}
题目有一些问题,理论上当 \(A,B\) 一位为 \(0\) 时,是不会繁殖的。但这题视为会繁殖,那就这样处理吧。
遍历一遍整棵树,不难发现,每个节点的松鼠只会遇到同一层的松鼠。而且根据分配律可证明(先挖坑,自己还没想明白),不论同一层的松鼠怎么分配,它们最后对答案的贡献;即为它们的和,乘上层数次(根节点为 \(1\) 层)的矩阵 \(\left( \begin{matrix} 1&C \\\ \D&1 \end{matrix} \right)\)
即第 \(n\) 层对答案的贡献为 \(\left( \begin{matrix} 1&C \\\ \D&1 \end{matrix} \right)^n\cdot \left( \begin{matrix} \sum_{dep_i=n}A_i \\\ \\sum_{dep_i=n}B_i \end{matrix} \right)\)
预处理出每一层对总和 \(A,B\) 的贡献,矩阵的乘方可以递推,累加起来即是答案。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7,MAXN=1e5+10;
struct Matrix{
ll Num[2][2];
Matrix() { Num[0][0]=Num[0][1]=Num[1][0]=Num[1][1]=0; }
Matrix operator * (const Matrix &x) const {
Matrix y;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
y.Num[i][j]=(y.Num[i][j]+Num[i][k]*x.Num[k][j])%MOD;
return y;
}
Matrix operator + (const Matrix &x) const {
Matrix y;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
y.Num[i][j]=(Num[i][j]+x.Num[i][j])%MOD;
return y;
}
}Bas,Dep[MAXN],Mul,Ans;
ll N,S,C,D,A[MAXN],B[MAXN];
vector<int> Edg[MAXN];
void dfs(int pos,int pa,int fl){
Dep[fl].Num[0][0]+=A[pos];
Dep[fl].Num[1][0]+=B[pos];
for(auto to : Edg[pos])
if(to!=pa)
dfs(to,pos,fl+1);
}
inline void pre(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>N>>S>>C>>D;
for(int i=1;i<N;i++){
int u,v;
cin>>u>>v;
Edg[u].push_back(v);
Edg[v].push_back(u);
}
for(int i=1;i<=N;i++) cin>>A[i]>>B[i];
Bas.Num[0][0]=1;
Bas.Num[0][1]=C;
Bas.Num[1][0]=D;
Bas.Num[1][1]=1;
Mul.Num[0][0]=1;
Mul.Num[1][1]=1;
dfs(S,0,1);
}
int main(){
pre();
for(int i=1;i<=N;i++){
Mul=Mul*Bas;
Ans=Ans+Mul*Dep[i];
}
cout<<(Ans.Num[0][0]+Ans.Num[1][0])%MOD<<endl;
return 0;
}
2020.07.24 福州大学ACM/ICPC集训队选拔赛 部分题解
标签:排队 sqrt second ons std 一个 节点 top 规律
原文地址:https://www.cnblogs.com/JustinRochester/p/13373163.html