感觉最近有些不在状态,几场考试考的都不是非常好.
然后我这种大弱渣显然只能去P啦~~~
(虽然觉得ACM赛制还是要跪
不过总要做出选择吗.
POJ1832
首先递推算出fi表示第i位到第
利用数学归纳法可以得到:
若有一个在第i位的
从而递推式就很容易写出来了.(我太懒就略过了
然后我们可以模拟变的过程,首先找到最高的不相同的位,然后对后面调用g,然后后面就变成了
显然需要高精度.
另外还有一种黑科技就是求出格雷码并转化为十进制数直接求差的绝对值即可.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
static const int mod=1e4;
struct Hugeint{
int d[1000],l;
Hugeint():l(1){
memset(d,0,sizeof d);
}
inline void operator*=(const int&x){
int t=0;
for(int i=0;i<l;++i){
t+=d[i]*x;
d[i]=t%mod;
t/=mod;
}
if(t)
d[l++]=t;
}
inline void operator+=(const Hugeint&B){
l=l>B.l?l:B.l;
for(int i=0;i<l;++i){
d[i]+=B.d[i];
if(d[i]>=mod){
d[i]-=mod;
d[i+1]++;
}
}
if(d[l])
++l;
}
inline void output(){
printf("%d",d[l-1]);
for(int i=l-2;i>=0;--i)
printf("%04d",d[i]);
}
};
Hugeint pow[130];
int a[128],b[128];
Hugeint f[130],g[130];
int main(){
//freopen("tt.in","r",stdin);
int T,n,i,j;
pow[0].d[0]=1;
for(i=1;i<128;++i){
pow[i]=pow[i-1];
pow[i]*=2;
}
cin>>T;
for(int Tcase=1;Tcase<=T;++Tcase){
cin>>n;
for(i=n-1;i>=0;--i)
cin>>a[i];
for(i=n-1;i>=0;--i)
cin>>b[i];
f[0].d[0]=g[0].d[0]=0;
if(a[0]==0)
g[0].d[0]=1;
else
f[0].d[0]=1;
for(i=1;i<n;++i){
if(a[i]==0){
f[i]=f[i-1];
g[i]=g[i-1],g[i]+=pow[i];
}
else{
f[i]=g[i-1],f[i]+=pow[i];
g[i]=f[i-1];
}
}
Hugeint res;
int ins=-1;
for(i=n-1;i>=0;--i)
if(a[i]!=b[i]){
ins=i;
break;
}
if(ins<0)
puts("0");
else{
if(ins)
res+=g[ins-1];
res+=pow[0];
if(ins>0){
memset(a,0,sizeof a);
a[ins-1]=1;
for(i=ins-1;i>=0;--i){
if(a[i]!=b[i]){
res+=pow[i];
if(i)
a[i-1]=1;
}
}
}
res.output();
puts("");
}
}
return 0;
}
POJ1112
首先建立反图,若两个点之间有连边则证明不能在一个集合中.
这让我们联想到二分图.
于是对于每一个联通分量进行二染色,若存在一个连通分量不能二染色则无解.
注意题目让我们找出一组最接近的解.
对于每个联通分量,我们将染色的方案存下来,那么有用的仅仅是两个部分分别属于哪个集合.
我们做一次简单的dp并记录方案即可.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define N 110
int head[N],next[N*N<<1],end[N*N<<1];
inline void addedge(int a,int b){
static int q=1;
end[q]=b;
next[q]=head[a];
head[a]=q++;
}
inline void make(int a,int b){
addedge(a,b);
addedge(b,a);
}
bool G[N][N];
int v[N];
int cnt;
vector<int>s[N][2];
bool f[N][N],g[N][N];
inline bool dfs(int x){
if(v[x]==-1){
v[x]=1;
s[cnt][1].push_back(x);
}
for(int j=head[x];j;j=next[j]){
if(v[end[j]]==-1){
v[end[j]]=1-v[x];
s[cnt][1-v[x]].push_back(end[j]);
if(!dfs(end[j]))
return 0;
}
else if(v[end[j]]==v[x])
return 0;
}
return 1;
}
inline int _abs(int x){
return x<0?-x:x;
}
bool ok[N];
inline void find(int x,int y){
if(x==0)
return;
if(g[x][y]==0){
for(int i=0;i<s[x][0].size();++i)
ok[s[x][0][i]]=1;
find(x-1,y-s[x][0].size());
}
else{
for(int i=0;i<s[x][1].size();++i)
ok[s[x][1][i]]=1;
find(x-1,y-s[x][1].size());
}
}
int main(){
int n;
scanf("%d",&n);
int i,j,x;
for(i=1;i<=n;++i){
while(scanf("%d",&x)&&x)
G[i][x]=1;
}
for(i=1;i<=n;++i)
for(j=i+1;j<=n;++j)
if(!(G[i][j]&&G[j][i]))
make(i,j);
memset(v,-1,sizeof v);
bool nosol=0;
for(i=1;i<=n;++i){
if(v[i]==-1){
++cnt;
if(!dfs(i)){
nosol=1;
break;
}
}
}
if(nosol)
puts("No solution");
else{
f[1][s[1][1].size()]=1;
g[1][s[1][1].size()]=1;
f[1][s[1][0].size()]=1;
g[1][s[1][0].size()]=0;
for(i=1;i<cnt;++i)
for(j=0;j<=n;++j)
if(f[i][j]){
f[i+1][j+s[i+1][0].size()]=1;
g[i+1][j+s[i+1][0].size()]=0;
f[i+1][j+s[i+1][1].size()]=1;
g[i+1][j+s[i+1][1].size()]=1;
}
int ans=0x3f3f3f3f,num;
for(i=0;i<=n;++i){
if(f[cnt][i]&&_abs(i-(n-i))<ans){
ans=_abs(i-(n-i));
num=i;
}
}
find(cnt,num);
vector<int>v1,v2;
for(i=1;i<=n;++i)
if(ok[i])
v1.push_back(i);
else
v2.push_back(i);
printf("%d",v1.size());
for(i=0;i<v1.size();++i)
printf(" %d",v1[i]);
puts("");
printf("%d",v2.size());
for(i=0;i<v2.size();++i)
printf(" %d",v2[i]);
}
return 0;
}
POJ2238
题目水的一比,只需要暴力枚举自己的四项属性,然后做一次dp看一看此时的获胜概率是多少就行了.
令fi,j,0,fi,j,1分别表示自己得到i分,另一个人得到
把fi,j,0,fi,j,1缩成一个强连通分量,不难发现状态转移图是一个拓扑图.
于是只需要套用一般的方法:对于每一个连通分量先处理这个连通分量里面的答案,然后将概率转移到后续的连通分量里面的点,然后再后面的连通分量里面再处理就行了.
对于一个连通分量,里面的转移是存在环的,因此要列方程进行求解.
对于分数二元组(i,j),令p0表示fi,j,0,令p1表示fi,j,1,则有以下的方程:
其中c0,c1表示从上面的连通分量转移下来的贡献.
随便解个方程就行了.
现在问题的关键是源点怎么转移.
(跪大爷
我们可以在两个方程中的一个加上p0=1,这样我们惊奇的发现得到正确的答案啦!
但是这样转移下去是并不对的!
上述的正确答案是指只有这个连通分量时的正确答案.
也就是说源点本可以以更大的概率转移到下面的分量.
因此我们需要将这个连通分量里面所有的点的概率都除以原点的概率!(雾
其实我得出的结论是:只要方程是对的,随便搞搞就能得出正确的结果啦!
然后也很容易对拍:不断迭代就行啦!
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef double db;
int n,m;
int pt2[2],pt3[2],reb[2],def[2];
db ans=0;
db f[35][35][2],g[35][35][2];
db ok3[2],ok2[2],stay[2],change[2];
typedef pair<int,int> pii;
int deg[35][35];
bool vis[35][35];
static const int dx[4]={2,0,3,0};
static const int dy[4]={0,2,0,3};
pair<db,db>solve(db a1,db b1,db c1,db a2,db b2,db c2){
db x,y;
y=(c1*a2-c2*a1)/(b1*a2-a1*b2);
x=(c1-b1*y)/a1;
return make_pair(x,y);
}
inline db dp(){
int i,j,k;
for(i=0;i<2;++i){
db p=pt3[i]/(db)(pt2[i]+pt3[i]);
ok3[i]=p*0.8*pt3[i]/(db)(pt3[i]+def[1-i]);
ok2[i]=(1-p)*pt2[i]/(db)(pt2[i]+def[1-i]);
stay[i]=(1-ok3[i]-ok2[i])*0.8*reb[i]/(db)(reb[0]+reb[1]);
change[i]=1-ok3[i]-ok2[i]-stay[i];
}
queue<pii>q;
q.push(make_pair(0,0));
memset(vis,0,sizeof vis);
vis[0][0]=1;
memset(deg,0,sizeof deg);
pii tmp;
while(!q.empty()){
tmp=q.front();
q.pop();
int x=tmp.first,y=tmp.second;
if(x>=n||y>=n)
continue;
for(i=0;i<4;++i){
++deg[x+dx[i]][y+dy[i]];
if(!vis[x+dx[i]][y+dy[i]]){
vis[x+dx[i]][y+dy[i]]=1;
q.push(make_pair(x+dx[i],y+dy[i]));
}
}
}
memset(f,0,sizeof f);
memset(g,0,sizeof g);
db re=0;
q.push(make_pair(0,0));
while(!q.empty()){
tmp=q.front();
q.pop();
int x=tmp.first,y=tmp.second;
pair<db,db>get;
if(x==0&&y==0){
get=solve(2-stay[0],-change[1],1,-change[0],1-stay[1],0);
get.second/=get.first;
get.first=1;
}
else
get=solve(1-stay[0],-change[1],g[x][y][0],-change[0],1-stay[1],g[x][y][1]);
f[x][y][0]=get.first;
f[x][y][1]=get.second;
if(x>=n||y>=n)
continue;
g[x+2][y][0]+=f[x][y][0]*ok2[0];
g[x][y+2][1]+=f[x][y][1]*ok2[1];
g[x+3][y][0]+=f[x][y][0]*ok3[0];
g[x][y+3][1]+=f[x][y][1]*ok3[1];
for(i=0;i<4;++i)
if(!(--deg[x+dx[i]][y+dy[i]]))
q.push(make_pair(x+dx[i],y+dy[i]));
}
for(j=0;j<=34;++j)
for(k=0;k<=34;++k)
if((j>=n||k>=n)&&(j>k))
re+=f[j][k][0];
return re;
}
inline void dfs(int dep,int last){
if(dep==5&&last==0){
ans=max(ans,dp());
return;
}
for(int i=1;i<=10&&i<=last;++i){
if(dep==1)
pt2[0]=i;
else if(dep==2)
pt3[0]=i;
else if(dep==3)
reb[0]=i;
else
def[0]=i;
if(dep==3){
if(last-i>=1&&last-i<=10)
dfs(dep+1,last-i);
}
else
dfs(dep+1,last-i);
}
}
int main(){
while(scanf("%d%d%d%d%d%d",&n,&m,&pt2[1],&pt3[1],&reb[1],&def[1])!=EOF){
ans=0;
dfs(1,m);
printf("%.3lf\n",ans);
}
return 0;
}
POJ2054
首先注意到限制等价于每个点的父亲都比这个点要早选择.
若没有任何限制,显然应该按照权值从大到小选择.
现在有限制,我们找到权值最大的那个点,那么我们期望它尽可能早的被选择.
那么他和他的父亲在选择序列中必定是连续的,因为如果不连续,我们可以将这个点的选择时间不断前移,依旧满足所有限制条件并且答案不会增大.
那么我们可以把这两个点缩成一个点!这个点权值为原来所有点的平均值.
为什么是平均值呢?
不妨举一个简单的例子:
考虑两个连通分量s1,s2,按照已经确定的顺序,里面权值的顺序分别是:
s1:x1,x2
s2:x3,x4,x5
考虑若s1,s2优于s2,s1,这证明:
于是得到:
由于我们是要把权值较大的排在前面,因此我们能够发现这样重新计算权值是合理的.
于是随便乱搞就行了.(窝是口胡选手
POJ3420
状压dp+矩阵乘法
POJ4047
题目的意思是维护一个序列,支持单点修改,支持交换两个位置的元素,并询问一段区间内总和最大的长度为k的子段的和是多少.
一开始看成了在区间内找恰好
这个可以模拟费用流利用线段树维护,可以在O(∑klogn)的时间内出解.
但是这样是不超过k个子段,而不是恰好
至于怎么处理“恰好”,我想到了一个比较捉急的方法,我们可以在增广k次的前提下,再增广一次,如果答案不变,那么贪心选择区间前
回到原题,线段树傻逼题,就不说了.
POJ1647
细节题+大模拟.
(感觉不是很麻烦?随便写写就行了.
POJ1818
首先答案显然满足二分性质,不妨令x为当前判定的元素.
考虑最后一轮,在最优意义下,
这是因为x−k是能被x打败且最容易留到最后一轮的元素.
再考虑倒数第二轮?
同样,给这两个元素分配他们能打败的最强的对手.
于是这样一轮一轮的分配,若所有人都被分配到了,证明存在一组可行解.
POJ3986
题意:
求
数据范围n≤50,yi,m≤109.
感觉对于这种思路还不是很熟悉吧…有点脑残.
首先对于所有数排序,找到最大的那个数的最高非零位.
仅仅考虑这一位,如果最终结果的这一位为0,剩下的数(除了最大的数的那些数)异或起来这一位也为
对于这个,我们可以用一个简单的dp来计算.注意要满足限制.
现在问题来了:如果要求异或和这一位为
那么限制最大的那个数的选择方案依然是唯一的———但却不一定能够取到!
不过我们已经确定了这个数这一位必定要选
那么我们只需要确定这个数后面的那些位怎么选就行了.
于是我们可以将这个问题转化为一个和刚才相似的子问题:
若最大的数的限制是x,且最高非零位是第
于是这样就能递归做下去了.
一些边界问题见代码.
(SRM564div1 850pts)
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
using namespace std;
typedef long long ll;
static const int mod=(1e9)+7;
inline void inc(int&x,int y){
if((x+=y)>=mod)
x-=mod;
}
int f[51][2];
class DefectiveAddition {
public:
int count(vector <int> cards, int m) {
int n=cards.size();
sort(cards.begin(),cards.end());
int t;
for(int i=31;i>=0;--i)
if((cards[n-1]>>i)&1){
t=1<<i;
break;
}
if(cards[n-1]==0)
return m==0;
if(m>=(t<<1))
return 0;
memset(f,0,sizeof f);
f[0][0]=1;
for(int i=0;i<n-1;++i)
for(int j=0;j<2;++j){
inc(f[i+1][j],(ll)f[i][j]*min(cards[i]+1,t)%mod);
inc(f[i+1][j],(ll)f[i][j^1]*max(0,cards[i]-t+1)%mod);
}
int ans=f[n-1][((m&t)>0)?1:0];
cards[n-1]-=t;
inc(ans,count(cards,m^t));
return ans;
}
};
POJ2906
ST表优化+网络流
POJ3585
题意:给定一棵树,树上的每一条边都有流量限制,每个点最大流量值等于这个点到所有剩下的叶子节点的最大流.求所有点的最大流量值的最大值.
水水的树形dp啊.
POJ1777
数学题.
重要定理:一个数
由于在数据范围之内梅森素数非常少,随后只要看一个数拥有哪些梅森素数,然后状压dp就行了.
[WF2013]Self-Assembly
将一个正方形看成一个点,若两个正方形之间可以有一种方式连接则从+的一个正方形向
可以证明,若最终的图存在一个环,则可以无限构造下去.
但是这样边数和点数都太多了.
我们注意到有用的只有52个点.
在一个正方形内部,比如说有A+,B−,那我们连接A+→B+,B−→A−,这样的话如果这个图有环的话也可以证明可以无限构造.
这样就可以了.
[WF2013]Surely You Congest
首先需要注意到到点1距离不同的点之间不可能互相干扰.
这是因为他们同时出发,每次只走最短路,不可能同时走到和点
那么仅需要考虑的是到点1距离相同的所有点.
建出最短路图,设流量限制为
[WF2013]Factors
发现可行的状态数很少,于是预处理一个表然后在表里面找.
[WF2013]Low Power
首先排序,二分答案,然后尽可能选择靠前的满足条件的连续对.然后判定一下在此种选择之下能否将那些较大的分配出去.
判定条件自己想想(窝TM连这东西都不会啦!QwQ
BZOJ3777-3779已经风化在历史中…
(手贱没存盘,题解就这样没了QwQ
POJ1708
令f[i][j]表示一个到达i,另外一个到达
[WF2013]Матрёшка
区间dp,令f[i][j]表示将区间[i,j]里面的套娃合并成一个大套娃的最小代价.
考虑转移f[i][k]以及f[k+1][j],若两端区间里面的最大值相同,显然不能合成一个套娃.值为INF.
否则进行分类讨论:
另两个区间最小值分别为x1,x2,不妨令x1≤x2.
若x1<x2,则代价等于总区间长度减去x1所在区间中值在[x1,x2−1]的个数.
若x1=x2,则代价等于总区间长度减去两个区间中值为x1的个数.
这东西怎么算?
首先O(n3)预处理出g[i][j][k]表示前k个数中值在
于是回答上面的问题只需要O(1)利用前缀和相减就行了.
求区间最值暴力预处理出来就行了.
于是时间复杂度为O(n3).
[WF2013]Pirate Chest
首先令箱子底部为w∗l,高度为h,同时放置区域的深度最小值为
整理一下得到:
所以有h=⌊nmd−1nm−wl⌋.
考虑w,d确定时,l越大
考虑枚举
于是时间复杂度为O(n3).
[WF2013]Harvard
(感觉这些所有的东西都是在扒贾教题解…
直接枚举集合划分.
然后对于两个连续的访问,若两个元素所在的区域不同且后一个元素不在0号区域那么就会带来
可以预处理出f[i][j]表示i,j两个变量连续访问的次数.
集合划分的枚举量不超过B13,又由于变量最多有13个,于是复杂度就是O(132B13).
又由于时间限制为10s,大概这样就能过了.
POJ3594
拆点最短路即视感(感觉能被卡掉?
POJ3595
推了一下午公式,然后发现是错的而且根本不知道哪里不对.
学到一个近似公式n!=2πn−−−√(ne)neλn,其中112n+1<λn<112n.
然而我依然不知道我的公式哪里不对TAT.
知道这个姿势就行了吧哈哈哈哈.
upd:其实这道题目还是不错的.
首先要找出递推公式:fn=(2n−5)fn−1,递归边界是f3=1.
为什么是这个呢?
考虑现在我们要加入一个叶子以及一个中间节点,首先他们两个一定要连在一下,然后中间节点还有两个度谁来提供呢?
原来的树都是满度数的,我们考虑任意断去一条边,然后用这个中间节点来代替这两条边,这样就可以了.
而原来的树的边数为2(n−2)−2−1=2n−5,这个式子就是这样来的.
然后能够得到:fn=(2n−5)!2n−3(n−3)!.
所以有log(fn)=log((2n−5)!)−log((n−3)!)−(n−3)log2.
那么log(n!)等于什么呢?
根据上面的公式,log(n!)=0.5(log(2π)+logn)+n(logn−1)+λn.
然后我们就有logefn辣!
然后随便推一下式子变成log10fn.
然后这个东西的整数部分就是E后面的数!这个东西的小数部分就是前面的数!
使用一个神奇的函数
然后再随便搞搞就行了.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double f2;
inline f2 log_fac(int n){
return 1.0/(12*n)+n*(log((f2)n)-1)+0.5*(log(2*acos(-1.0))+log((f2)n));
}
inline void work(int n){
if(n<=10){
ll ans=1;
for(int i=3;i<=n;++i)
ans*=(2*i-5);
int pos=0;
ll tmp=ans;
while(1){
if(tmp<10)
break;
++pos;
tmp/=10;
}
ll _tmp=ans*1000;
for(int i=1;i<=pos;++i)
_tmp/=10;
printf("%d.%03dE%d\n",(int)_tmp/1000,(int)_tmp%1000,pos);
}
else{
f2 log_e=log_fac(2*n-5)-log_fac(n-3)-(n-3)*log(2.0);
f2 log_10=log_e/log(10.0);
f2 num;
f2 last=modf(log_10,&num);
printf("%.3lfE%d\n",exp(last*log(10.0)),(int)num);
}
}
int main(){
int n;
while(scanf("%d",&n)!=EOF)
work(n);
return 0;
}
*POJ3596
三维计算几何我并不会.
(感觉POJ月赛真是千古大坑…三无产品毛都没有啊QoQ
[NEERC14]A
简单的贪心一下,感觉没有什么不对的.
BZOJ2280: [Poi2011]Plot
首先二分答案,然后如果直接按照随机增量法做最小圆覆盖,依次尝试添加点,肯定会被卡成最坏O(n3)的情况,就完蛋了.
然后如果二分呢?
令划分的最小段数为m,这样的复杂度最坏是
于是我们可以对于当前位置,枚举找出一个整数k,使得以当前位置为起点,长度为
然后就可以在其中二分了.
这样可以保证时间复杂度是O(nlogn)的!
于是总时间复杂度就是O(nlog2n)的了.
(插播广告之Lucas定理的证明
不妨令n=∑ki=0ni⋅pi,m=∑ki=0mi⋅pi,0≤ni,mi<p,0≤m≤n.
然后现在我们要证明的是:
首先有一个科学的等式,若p为质数则有:
这是因为左侧对于任意1≤i<p,xi的系数为Cip,然后这东西等于p!i!(p−i)!,肉眼观察一下上面的p是不可能除掉的,于是这东西就能被
所以这个等式是成立的.
然后考虑下面的式子:
然后考虑两边xm的系数.
左边肯定是Cmn.
考虑右侧每一项的二项式展开,就是∑nij=0Cjnixj⋅pi.
注意我们最终是要得到xm的系数,考虑将m进行
把它们的系数都找出来,我们就得到了我们要证明的式子:
[UOJ#86]mx的组合数
(大家好我是BB选手
我们上面刚证完Lucas定理,现在就要用了.
首先考虑差分,这样限制就只有≤r了.
由于p是素数,在
首先将n进行
由于k只有
接下来进行数位dp,令f[i][j]表示p进制后
直接暴力转移是O(p2logp)的.
我们考虑求出p的原根
这样f[i]就能在O(plogp)的时间内由f[i−1]进行一次卷积得到.
这样O(plog2p)就能完成预处理.
然后利用数位dp的思想按位确定就行了.
合并的时候也利用卷积来搞.
这样的总时间复杂度就是O(plog2p).
然后有一个小问题就是余数为0是没有原根的.这东西简单记录一下就好了吧.
[2015.5.23]A
题目大题:有一个二叉树,除了叶子节点的节点都有两个子结点.节点
题解:不妨令
令F(i)表示i的子树内部的dp全部做完花费的总时间代价,
我们可以证明:
对于叶子节点i,
对于非叶子节点i,有:
对于根节点root使用此式子,由于siz(root)<n,因此就有F(root)<n2.
于是就可以了.
[2015.5.23]B
题目大意:有n1种两两不同的A舰以及
数据范围:1sec,T≤200,1≤n1,n2,n3≤1018,m,p≤103.
题解:若令A舰选择
让我们把以上的这些数都在mod进制下考虑.
我们知道m1+m2=n3,假如他们相加的过程中在mod进制下进位了,那么意味着存在某一位的m1i+m2i=mod+n3i,于是有m1i=mod−m2i+n3i>n3i.
而根据lucas定理,Cm1n3存在Cm1in3i这个因数,于是在这种情况下的方案数对mod取模余数为0,可以不用考虑!
如果没有进位那就好开心了是不是?直接对于每一位考虑即可.
对于每一位枚举
最后将每一位的数组卷积起来就行了.
(懒不想算复杂度了
[2015.5.23]C
题目大意:给定一棵边带权的树,对于每个点求出经过这个点的所有简单路径的异或和的最大值.
数据范围:
思路:考虑点分治,在分治结构中处理出根的所有子树的前缀Trie以及后缀Trie,这样就能支持询问每个点为端点经过根的最大异或和路径.然后用这个值来更新这个分治结构中这个点的所有祖先.不过这样为什么就能正确更新答案了呢?(由于我懒)大家就好好思考一下吧: )
时间复杂度O(32nlogn).
POJ3597
题目大意:求将一个凸n边形划分为若干个三角形和四边形的方案数.
题解:我直到现在才明白将一个凸
结果我纠结了好久方案重复的问题…
然后这道题也是一样的,就是固定一条边,然后考虑这条边对着的是什么.
如果是三角形,那我们直接用刚才的方法.
如果是四边形,那我们需要枚举一对二元组,我们可以枚举i表示二元组中的第一个点到固定的边的第一个点的距离,再枚举
不妨令fn表示将凸n边形进行上述划分的方案数,然后有以下递推式:
然后发现是O(n3)的滚粗了.
我们再令gn=∑n−3j=1fj+1fn−j−1,然后原式就可以变成:
在计算f的同时顺便计算
边界什么的直接手玩.
POJ3598
妈呀这题我都不会真是日狗了.
首先将所有点按照x为第一关键字,
将这个过程利用线段树加速就行了.
(我是傻逼
BZOJ2613
题意:给定一个置换b以及正整数
想法:考虑将a进行分解,分解成若干个循环,对于每个长度为
我们对于b进行循环分解,对于每种长度相同且为
这是为什么呢?
考虑
由于题目保证有解,我们只需要找出最小的L,并将所有长度为
怎么找出最小的
【弱省胡策】Round #0 A
20%数据,O(n4)傻逼dp.
40%数据,O(n3)傻逼dp.
100%数据,令f(x1,y1,x2,y2)表示从(x1,y1)走到(x2,y2)的路径条数.于是所有路径就是f(1,2,n−1,m)×f(2,1,n,m−1).然而两条路径可能在中间的某个点相交,我们找出最早的交点,并在这个交点互换两条路径的后半部分,这样就成了一条(1,2)到(n,m−1)的路径以及一条(2,1)到(n−1,m)的路径,而且两者之间是一一对应关系.
于是答案等于:
简单O(n2)dp处理出四个量即可.
【弱省胡策】Round #0 B
首先要知道什么是DFA,就是一个自动机,在相同的转移下到达的节点一定相同.
分别建立两个串的后缀自动机以及子序列自动机,令fi,j表示在自动机1中转移到了
由于是DFA,这里的转移显然不可能出现重复的串.
于是就是O(|sigma|n2)傻逼dp.
我图(作)方(大)便(死)写了傻逼hash结果变成傻逼.
给人感觉好像不能严格n2?
JZPLCM
题目大意:给定一个正整数序列,每次询问一段区间内的所有数的最小公倍数对109+7取模的余数.
数据范围:n≤105,保证所有数都能够分解成不超过100的质数的乘积.
解法:对所有数质因数分解,若有一个pq的因子,则相当于这个位置有p,p2,p3,...,pq这q个数出现,同时他们出现的代价都是
而这是一个经典问题.
数的总数仅有O(nlogn)个,我们直接采用主席树维护即可.
若需要支持修改则可以用树状数组套权值线段树来维护.
CF Round305 div1A
玛德考场上写这题真是炖了狗了.
首先显然是找循环,然后我转化成了求二元一次方程a1x+b1=a2y+b2使得x,y≤0且最小的整数解.
于是就坑了接近一个小时,最终没有通过pretest.
事实证明这种方法(如果是我来写的话)必然要经过大量的分类讨论.
我们来看一种简单的方法吧.
首先对于h1暴力模拟,找出最早变成a1的时间q,若
于是我们就在O(m)的时间里解决了这道傻逼题.
CF Round305 div1B
首先注意到,对于i,
于是我们按照权值从大到小插入所有数,并维护当前的最长连续段即可.
这一步可以简单地拿一个并查集来维护.
时间复杂度
CF Round305 div1C
令f(x)表示当前存在的所有数是x倍数的个数,于是互质的对数等于:
考虑插入或者删除一个数,仅仅需要对于上式中他的约数进行修改.
而数字最大只有5×105,约数个数是很少的,我们直接暴力修改就可以通过.
不过注意找出约数的复杂度必须是严格与约数个数相关的.
*CF Round305 div1D
(坑)
CF Round305 div1E
裸题+模板题.
我的思路:首先建立广义后缀树,然后问题就是问子树内的信息问题.
这样就是O(nlogn)的.
但是也可以建立后缀数组,然后通过预处理RMQ,上下二分得到子串所在的区间,于是这样一个位置只对应一个标号,就可以轻易拿主席树处理出来了.
这样也是O(nlogn)的.