————————————————————————————20181030
还有10天就noip了,还有6天就要被半期摧残了……
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊~~~~~~~~~~~~~~~~~
还是写题解吧……~~
T1 排列树
【WOJ4163】
排列组合
数学推理
从根往下搜,每一棵子树的根都记录一个siz大小。
每一棵子树的根,我们都要分配一个最小的值,
然后一个一个儿子地搜,ans乘上我们可以给这棵子树分配多少种不同的数,
再乘上子树内部的方案数。
因为阶乘取模之后再除答案就不对了,所以用逆元取模
- a / b = a * bmod-2 % mod;【mod是个大质数】
一定要预处理阶乘和逆元!!!
#include<iostream>
#include<cstdio>
#include<cctype>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return f==1?x:-x;
}
#define mod 998244353
struct edge{
int v,nxt;
}e[200005];
int first[100005],cnt=0;
inline void add(int u,int v){
e[++cnt].v=v;
e[cnt].nxt=first[u];first[u]=cnt;
}
int n,siz[100005],ans[100005];
int mul1[100005],mul2[100005];//阶乘 逆元
int ksm(int x){
int b=mod-2,res=1;
while(b>0){
if(b&1)res=1ll*res*x%mod;
x=1ll*x*x%mod;
b>>=1;
}
return res;
}
int mul(int x,int y){
return 1ll*mul1[x]*mul2[x-y]%mod*mul2[y]%mod;
}
void dfs(int x,int fa){
for(int i=first[x];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa)continue;
dfs(v,x);
siz[x]+=siz[v];
}
ans[x]=1;
int c=0;
for(int i=first[x];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa)continue;
ans[x]=1ll*ans[x]%mod*mul(siz[x]-c,siz[v])%mod*ans[v]%mod;
c+=siz[v];
}
siz[x]++;
}
int main(){
n=read();
mul1[0]=mul2[0]=1;
for(int i=1;i<=n;i++){
mul1[i]=1ll*mul1[i-1]*i%mod;
mul2[i]=ksm(mul1[i]);
}
for(int i=1;i<n;i++){
int u,v;
u=read();v=read();
add(u,v);add(v,u);
}
dfs(1,0);
printf("%d",ans[1]);
return 0;
}
T2 字胡串
【WOJ4164】
单调栈
智商题
线性DP
对于每一个区间,最大值是二进制下0的位置其它数有至少一个为1,
那或起来就大于max,就是合法区间。
所以我们用单调栈找到每一个数左边离自己最近的严格比自己大的数和右边离自己最近的大于等于自己的数。
【注意一个是大于等于,一个是大于,避免计算重复】
再用线性DP找到左边离自己最近的或(|)自己更大的数和右边离自己最近的或(|)自己更大的数。
如果或在大的值里面就计算答案,注意重复。
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int n,a[N];
stack<int>s;
int l[N],r[N];
//左边离自己最近的严格比自己大的数 右边离自己最近的大于等于自己的数
int dl[N],dr[N];
//左边离自己最近的或(|)自己更大的数 右边离自己最近的或(|)自己更大的数
int mx[50],mn[50];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++){//L
while(!s.empty()&&a[i]>=a[s.top()])s.pop();//一个>=一个> 避免重复
l[i]=(s.empty()?1:s.top()+1);
s.push(i);
}
while(!s.empty())s.pop();
for(int i=n;i>0;i--){//R
while(!s.empty()&&a[i]>a[s.top()])s.pop();
r[i]=(s.empty()?n:s.top()-1);
s.push(i);
}
for(int i=1;i<=n;i++){//DL
dl[i]=0;
for(int j=0;(1<<j)<=a[i];j++){
if(a[i]&(1<<j))mx[j]=max(mx[j],i);
else dl[i]=max(dl[i],mx[j]);
}
}
fill(mn,mn+40,n+1);
for(int i=n;i>0;i--){//DR
dr[i]=n+1;
for(int j=0;(1<<j)<=a[i];j++){
if(a[i]&(1<<j))mn[j]=min(mn[j],i);
else dr[i]=min(dr[i],mn[j]);
}
}
long long ans=0;
for(int i=1;i<=n;i++){//ANS
if(dl[i]>=l[i])ans+=1ll*(dl[i]-l[i]+1)*(r[i]-i+1);
if(dr[i]<=r[i])ans+=1ll*(r[i]-dr[i]+1)*(i-l[i]+1);
if(dl[i]>=l[i]&&dr[i]<=r[i])ans-=1ll*(dl[i]-l[i]+1)*(r[i]-dr[i]+1);
}
printf("%lld",ans);
return 0;
}
T3 有环无向图
【WOJ4165】
最短路
智商题
struct E{
int x,y;
E(){}
E(int a,int b):x(a),y(b);
};
a,b可换
可以直接用
a=E(e,f);
赋值,相当于
a.x=e;a.y=f;
先将边从小到大排序,相邻的两条边(把边当成点)建边
根据题中要求,
向比自己的边连一条0的边,向比自己大的边连一条Wj+1-Wj的边,
再向自己的反向边连一条Wj的边,避免算出来全是差值,
再特殊处理起点和终点。
这道题有点绕,我现在都还是晕乎乎的~~
#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define inf 1000000000000000000
int n,m,t=1;
priority_queue<pair<long long,int> >q;
vector<pair<int,int> >vec[N];//
struct edge{
int id,val;
edge(){}//
edge(int id,int val):id(id),val(val){}
friend inline bool operator <(const edge &a,const edge &b){
return a.val<b.val;
}
}a[N];
struct E{
int u,v,w,nxt;
}e[N];
int first[N],cnt=1;
inline void add(int u,int v,int w){
e[++cnt].u=u;e[cnt].v=v;e[cnt].w=w;
e[cnt].nxt=first[u];first[u]=cnt;
}
long long dis[N];
bool vis[N];
void dijkstra(){
for(int i=2;i<=cnt;i++)dis[i]=inf;
for(int i=first[1];i;i=e[i].nxt){
dis[i]=e[i].w;
q.push(make_pair(-dis[i],i));
}
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=0;i<vec[x].size();++i){
int v=vec[x][i].first,w=vec[x][i].second;
if(dis[v]>dis[x]+w){
dis[v]=dis[x]+w;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
for(int i=1;i<=n;i++){//
int num=0;
for(int j=first[i];j;j=e[j].nxt)
a[++num]=edge(j,e[j].w);
sort(a+1,a+num+1);
for(int j=1;j<=num;j++){
if(j!=1)vec[a[j].id].push_back(make_pair(a[j-1].id,0));
if(j!=num)vec[a[j].id].push_back(make_pair(a[j+1].id,a[j+1].val-a[j].val));
vec[a[j].id^1].push_back(make_pair(a[j].id,a[j].val));//连自己?
}
}
dijkstra();
long long ans=inf;
for(int i=first[n];i;i=e[i].nxt)
ans=min(ans,dis[i^1]+e[i].w);
printf("%lld",ans);
return 0;
}