第四套题
出题人:Bakser
神犇地址:Bakser学长的blog
T1 LICS
有一天 Bakser 在做数学题,对于一道题他验算了 n 遍,得到了 n
个结果。无聊的他将这 n 个结果排成了一个圆环,无聊的他又想求这
个环的最长上升子序列。
请你回答这个无聊的问题。
所谓环的上升子序列,指的是从某个位置开始, 按照顺时针顺序
读这个环,得到一个线性序列,它的上升子序列也是这个环的上升子
序列。最长上升子序列是它们中最长的一个。
输入格式:
第一行一个数 n,表示有多少个元素。 第二行 n 个数, ai 表示从位
置 1 开始按顺时针顺序读这个环读到的第 i 个数是什么。
输出格式:
一行,一个数表示最长上升子序列长度。
样例输入:
5
1 2 3 4 5
样例输出:
5
数据范围:
对于 30%的数据, n≤1000
对于 100%的数据, n≤50000,1≤ai≤109
所有数据均为随机生成
专门从国外论文上找来题虐我们真是…跪了
全场比赛就靠这个题骗了30分233
还好8s时限写个普通暴力也能得分
标算太神
听说美国80年代开始研究这东西结果一样的O(nn√logn)算法一场比赛被神犇们搞出来了Orz
//std by Bakser
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N(5e4+200);
typedef pair<int,int> pii;
int n,a[N],b[N],x[N],t[N],top,f1[N],f2[N];
inline int find(int s){
int len(0);
memset(x,0x7f,sizeof(x));x[0]=0;
for(int i(0);i<n;i++){
int pos;
if(i<=n-s)pos=s+i;
else pos=i-n+s;
int p=lower_bound(x,x+len+1,a[pos])-x;
while(x[p]>=a[pos])p--;
if(a[pos]<x[p+1])
x[p+1]=a[pos],t[p+1]=pos,len+=(p==len);
}
return len;
}
inline int b_s(int l,int r,int val){
int res=0;
for(;l<=r;){
int mid(l+r>>1);
if(x[mid]>val)l=mid+1,res=max(res,mid);
else r=mid-1;
}
return res;
}
int main(){
freopen("LICS.in","r",stdin);
freopen("LICS.out","w",stdout);
scanf("%d",&n);
for(int i(1);i<=n;i++)scanf("%d",a+i);
int len=find(1),ans=len;
for(int i(1);i<=len;i++){
int l=0,p(0),last(0);
memset(f1,0,sizeof(f1));
memset(f2,0,sizeof(f2));
memset(x,0x7f,sizeof(x));x[0]=0;
for(int j(0);j<n;j++){
p=(t[i]+j)%n;
if(!p)p=n;
f1[p]=0;
if(a[p]>=a[t[i]]){
int t=lower_bound(x,x+l+1,a[p])-x;
while(x[t]>=a[p])t--;
if(a[p]<x[t+1])
x[t+1]=a[p],l+=(t==l);
f1[p]=t+1;
}
f1[p]=max(f1[p],f1[last]);
last=p;
}
l=0;memset(x,0,sizeof(x));x[0]=0x7fffffff;
for(int j(0);j<n;j++){
p=(t[i]-j+n)%n;
if(!p)p=n;
f2[p]=0;
if(a[p]<=a[t[i]]){
int t=b_s(0,l,a[p]);
if(a[p]>x[t+1])
x[t+1]=a[p],l+=(t==l);
f2[p]=t+1;
}
f2[p]=max(f2[p],f2[last]);
last=p;
}
for(int i(1);i<=n;i++)
ans=max(ans,f1[i]+f2[i%n+1]-1);
}
printf("%d\n",ans);
return 0;
}
T2 Tree
Bakser 得到了一棵无根树,每个节点上有一个权值, 他决定在这
棵树上逗比。设这棵树上从 u 到 v 的路径长度为 l,这条路径上的点
权 构 成 一 个 序 列 {z0,? z1…zl } 。 则 这 条 路 径 的 权 值 定 义 为
z0 × k0 +? z1 ×? k1 +?…?+? zl ×? kl。如果这条路径的权值 MOD Y 等于 X,则
称这条路径为逗比的。否则称它为高冷的。
他想出了一个结论,如果路径(u,t)和(t,v)是逗比的,则路径(u,v)一
定是逗比的。如果路径(u,t)和(t,v)是高冷的,则路径(u,v)一定是高冷的。
显然这个结论是错的,但高冷(dòu bī)的他才不会承认这个结论是
错的,请你统计对于给定的树, 有多少个满足这个结论的三元组(u,t,v)。
输入格式:
第一行四个数 n、 Y、 k、 X, n 是这棵树的点数, Y、 k、 X 的含义如
题目描述所示。
第二行 n 个数: v1 、 v2……vn,。 vi 表示节点 i 的权值。
接下来 n-1 行, 每行两个数 u、 v, 表示存在一条从 u 到 v 的无向
边。
输出格式:
一行,一个数表示满足题目条件的三元组数目 。
样例输入:
3 5 2 1
4 3 1
1 2
2 3
样例输出:
14
数据范围:
对于 30%的数据, n≤100
对于 60%的数据, n≤1000
对于 100%的数据, n≤100000 , 2?≤ Y?≤? 109; 1?≤? k <? Y; 0?≤? X <? Y
考试时候看出来是点分治了但是不会点分治= =
后来写了链剖代替
再后来就写残了
看来链剖果然不是正确姿势QAQ
枚举三元组太蛋疼= =标算的代替方法看得我醉了
100分算法就是在这个基础上加上了点分治而已
//std by Bakser 点分治好可怕比我链剖还长
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <climits>
#include <vector>
#include <map>
using namespace std;
const int N(1e5+200);
typedef long long LL;
typedef vector<int>::iterator iter;
#define MAX(a,b) (((a)>(b))?(a):(b))
template<class T>
inline void read(T &x){
char c=0;
for(;c<‘0‘||c>‘9‘;c=getchar());
for(x=0;c>=‘0‘&&c<=‘9‘;c=getchar())x=(x*10+(c-‘0‘));
}
int n,w[N],X,K,MOD,point[N],sz[N],f[N],in[N],ou[N],up[N],need[N],down[N],rot,pw[N],inv[N],q[N],top,dep[N];
struct hh{int next,v;}edges[N*2];
bool vis[N];
LL ans(0);
vector<int> son[N];
map<int,int>from,to;
inline void addedge(int u,int v){
static int en(0);
edges[++en].next=point[u];point[u]=en;
edges[en].v=v;
}
inline LL power(LL x,LL k){
LL res(1);
for(;k;k>>=1){
if(k&1)res=res*x%MOD;
x=x*x%MOD;
}
return res;
}
inline void dfs_sz(int x,int fa){
sz[x]=1;f[x]=0;q[++top]=x;
for(int i(point[x]);i;i=edges[i].next)
if(!vis[edges[i].v]&&edges[i].v!=fa){
int v(edges[i].v);
dfs_sz(v,x);
sz[x]+=sz[v];
f[x]=MAX(f[x],sz[v]);
}
}
inline int find_focus(int x){
top=0;dfs_sz(x,0);
int res,tmp(INT_MAX);
for(int i(1);i<=top;i++){
int t=MAX(sz[x]-sz[q[i]],f[q[i]]);
if(t<tmp)tmp=t,res=q[i];
}
return res;
}
inline void dfs(int x,int fa){
for(int i(point[x]);i;i=edges[i].next)
if(!vis[edges[i].v]&&edges[i].v!=fa){
int v(edges[i].v);
dep[v]=dep[x]+1;
up[v]=((LL)up[x]*(LL)K%MOD+(LL)w[v])%MOD;
down[v]=(down[x]+(LL)w[v]*(LL)pw[dep[v]])%MOD;
dfs(v,x);
}
}
inline void get_sons(int x,int fa,int anc){
son[anc].push_back(x);
for(int i(point[x]);i;i=edges[i].next)
if(!vis[edges[i].v]&&edges[i].v!=fa)
get_sons(edges[i].v,x,anc);
}
inline int ask(map<int,int> &m,int x){
if(m.count(x))return m[x];
return 0;
}
inline void add(map<int,int> &m,int x,int f){
if(m.count(x))m[x]+=f;
else m[x]=f;
}
inline void calc(){
from.clear();to.clear();
for(int i(1);i<=top;i++)
for(iter j=son[q[i]].begin();j!=son[q[i]].end();j++)
add(to,down[*j],1);
for(int i(1);i<=top;i++){
vector<int> &x=son[q[i]];
for(iter j=x.begin();j!=x.end();j++)
add(to,down[*j],-1);
for(iter j=x.begin();j!=x.end();j++){
ou[*j]+=ask(to,need[*j]);
in[*j]+=ask(from,down[*j]);
}
for(iter j=x.begin();j!=x.end();j++)
add(from,need[*j],1);
}
}
inline void solve(int x){
vis[x=find_focus(x)]=1;
up[x]=down[x]=w[x];dep[x]=0;
dfs(x,0);
for(int i(1);i<=top;i++)
if(q[i]==x){if(up[x]==X)ou[x]++,in[x]++;}
else{
if(up[q[i]]==X)in[x]++,ou[q[i]]++;
if(down[q[i]]==X)ou[x]++,in[q[i]]++;
}top=0;
for(int i(point[x]);i;i=edges[i].next)
if(!vis[edges[i].v]){
int v(edges[i].v);
q[++top]=v;son[v].clear();get_sons(v,0,v);
for(iter j=son[v].begin();j!=son[v].end();++j)
need[*j]=((X-up[*j]+MOD)*(LL)inv[dep[*j]]+w[x])%MOD;
}
calc();
reverse(q+1,q+top+1);
calc();
for(int i(point[x]);i;i=edges[i].next)
if(!vis[edges[i].v])
solve(edges[i].v);
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);read(MOD);read(K);read(X);
for(int i(1);i<=n;i++)read(w[i]);
for(int i(1),u,v;i<n;i++){
read(u);read(v);
addedge(u,v);
addedge(v,u);
}
for(int i(pw[0]=1);i<=n;i++){
pw[i]=(pw[i-1]*(LL)K)%MOD;
inv[i]=power(pw[i],MOD-2);
}f[0]=INT_MAX;
solve(1);
for(int i(1);i<=n;i++){
ans+=((LL)in[i]*LL(n-in[i]))<<1;
ans+=((LL)ou[i]*LL(n-ou[i]))<<1;
ans+=(LL)in[i]*LL(n-ou[i])+(LL)ou[i]*LL(n-in[i]);
}
ans=(LL)n*n*n-(ans>>1);
cout<<ans<<endl;
return 0;
}
T3 Circle
Bakser 是一个大蒟蒻,有一天他随手画了一个 n 个点的环状图。
他觉得只有一个环不美观就又在中间加了一个点,并从环上的 n 个点
分别向中间这个点引了一条边。
现在他想知道这个 n+1 个点的图有多少种不同的生成树。
每个点被认为是本质相同的,所以两个生成树被认为是相同的,
当且仅当这两个生成树经过旋转可以完全重合。
但良心的他觉得上面那个条件太丧病了,所以在某些数据中你不
必考虑它。
输入格式:
一行三个正整数: n,m,f。 n 表示环上的点数(即整个图一共 n+1
个点) m 表示答案对 m 取模。 f 为 1 时表示不需要考虑旋转同构,为
0 时表示需要考虑。
输出格式:
一行一个数,表示答案对 m 取模后的结果。
样例输入:
1 2333333 1
样例输出 :
1
数据范围:
数据点编号
1-3 n≤100 m≤109 f=1
4-5 n≤108 m≤109 f=1
6-7 n≤105 m≤109 f=0
8-10 n≤109 m≤109 f=0
一直想用组合数学做这个题
还是没做出来…
Sunshine神犇打表找规律过50%真是太神了OTZ
50分正确姿势Matrix-Tree定理好像大家都不爱他
递推式太神OTZ
(P.S.知道了也写不好…因为不会写矩阵乘)
同构使用群论解决
↑
为什么觉得有点像→Longge的问题
//std by Bakser
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N(1e9);
const int M(40000);
int n,phi[M],prime[M],tot;
bool vis[M];
LL MOD;
inline LL mul(LL a,LL b){
LL res=0;
a=(a%MOD+MOD)%MOD;
b=(b%MOD+MOD)%MOD;
for(;b;b>>=1){
if(b&1)res=(res+a)%MOD;
a=(a<<1)%MOD;
}
return res;
}
struct Mat{
LL a[2][2];
inline Mat operator *(const Mat &b)const{
Mat c;
for(int i(0);i<2;i++)
for(int j(0);j<2;j++){
c.a[i][j]=0;
for(int k(0);k<2;k++)
c.a[i][j]=(c.a[i][j]+mul(a[i][k],b.a[k][j]))%MOD;
}
return c;
}
}base;
inline Mat power(Mat a,int k){
Mat res;
for(int i(0);i<2;i++)
for(int j(0);j<2;j++)
res.a[i][j]=(i==j);
for(;k;k>>=1){
if(k&1)res=res*a;
a=a*a;
}
return res;
}
inline void sieve(){
for(int i(2);i<=M;i++){
if(!vis[i])prime[++tot]=i;
for(int j(1);j<=tot&&i*prime[j]<=M;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
inline LL get_phi(int n){
LL res(n);
for(int i(1);i<=tot&&prime[i]*prime[i]<=n;i++)
if(n%prime[i]==0){
res=res/prime[i]*(prime[i]-1);
while(n%prime[i]==0)n/=prime[i];
}
if(n>1)res=res/n*(n-1);
return res%MOD;
}
inline LL get_T(int k){
if(k==1)return 1;
if(k==2)return 5;
Mat t=power(base,k-2);
LL f=((3*t.a[0][0])%MOD+t.a[1][0])%MOD;
LL g=(2*(((f-(3*t.a[0][1]+t.a[1][1])-1)%MOD+MOD)%MOD))%MOD;
return (g+f)%MOD;
}
inline LL calc(){
LL res=0;
for(int i(1);i*i<=n;i++)
if(n%i==0){
res=(res+mul(get_phi(i),get_T(n/i)))%MOD;
if((n/i)!=i)res=(res+mul(get_phi(n/i),get_T(i)))%MOD;
}
return res/n;
}
int main(){
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
sieve();int ff=0;
base.a[0][0]=3;base.a[0][1]=1;base.a[1][0]=-1;base.a[1][1]=0;
scanf("%d%I64d%d",&n,&MOD,&ff);
if(ff)printf("%I64d\n",get_T(n));
else{
MOD=(LL)n*MOD;
printf("%I64d\n",calc()%(MOD/n));
}
return 0;
}
最后暴力分也没得全QAQ
再也不看见题就瞎写链剖了QAQ