感觉最近有些不在状态,几场考试考的都不是非常好.
然后我这种大弱渣显然只能去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)
的.