版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sugar_free_mint/article/details/84993130
迟到的解题报告
JZOJ 5123 diyiti
分析
6根木棍,只能是3+1+1+1或者是2+2+1+1,所以分类讨论。(以下其它情况都排除了之前的情况,也就是容斥,为了行文方便,在此不多写)
设边长为
(
代表
)
2+2+1+1的组合方式 |
---|
以下保证 |
3+1+1+1的组合方式 |
---|
这样讲好像很简单,实际上实践会比较容易在细节上出问题,我的细节出错点详见代码
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <vector>
#define rr register
using namespace std;
const int N=10000001;
typedef long long ll; ll ans;
vector<int>t; int cnt[N+10],dcnt[N+10];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
signed main(){
freopen("yist.in","r",stdin);
freopen("yist.out","w",stdout);
for (rr int n=iut(),x;n;--n)
if (++cnt[x=iut()]==1) t.push_back(x);
sort(t.begin(),t.end());
for (rr int i=0;i<t.size()-1;++i)
for (rr int j=i+1;j<t.size();++j)
if (t[i]+t[j]<N)
dcnt[t[i]+t[j]]+=cnt[t[i]]*cnt[t[j]];//原代码只是=
else break;
for (rr int i=1;i<t.size();++i){
if (cnt[t[i]]==1) continue;
if (cnt[t[i]]>1){
rr ll x=1ll*cnt[t[i]]*(cnt[t[i]]-1)>>1,sum=0;
if (!(t[i]&1)&&cnt[t[i]>>1]>3)
ans+=x*cnt[t[i]>>1]*(cnt[t[i]>>1]-1)*(cnt[t[i]>>1]-2)*(cnt[t[i]>>1]-3)/3>>3;//原代码判成奇数
for (rr int j=0;j<i;++j)
if ((t[j]<<1)<t[i]){
if (cnt[t[j]]>1&&cnt[t[i]-t[j]]>1)
sum+=1ll*cnt[t[j]]*(cnt[t[j]]-1)*cnt[t[i]-t[j]]*(cnt[t[i]-t[j]]-1)>>2;
}else break;
ans+=x*sum; sum=0;
for (rr int j=0;j<i;++j)
if ((t[j]<<1)<t[i]){
if (cnt[t[i]-t[j]])
ans+=x*sum*cnt[t[j]]*cnt[t[i]-t[j]],sum+=cnt[t[j]]*cnt[t[i]-t[j]];
}else break;
if (!(t[i]&1)) ans+=x*sum*cnt[t[i]>>1]*(cnt[t[i]>>1]-1)>>1;//原代码未判断
}
if (cnt[t[i]]==2) continue;
if (cnt[t[i]]>2){
rr ll x=1ll*cnt[t[i]]*(cnt[t[i]]-1)*(cnt[t[i]]-2)/3>>1,sum=0;
if (t[i]%3==0&&cnt[t[i]/3]>2)
ans+=x*cnt[t[i]/3]*(cnt[t[i]/3]-1)*(cnt[t[i]/3]-2)/3>>1;
for (rr int j=0;j<i;++j){
sum+=1ll*cnt[t[j]]*dcnt[t[i]-t[j]];
if ((t[j]<<1)<t[i]&&t[j]*3!=t[i])
sum-=1ll*cnt[t[j]]*cnt[t[j]]*cnt[t[i]-(t[j]<<1)];
}
ans+=x*sum/3;
for (rr int j=0;j<i;++j)
if ((t[j]<<1)<t[i]){
if (t[j]*3!=t[i]) ans+=x*cnt[t[j]]*(cnt[t[j]]-1)*cnt[t[i]-(t[j]<<1)]>>1;
}else break;
}
}
return !printf("%lld",ans);
}
JZOJ 100042 保留道路
题目大意
个点 条边的无向图中,每条边都有两个属性g和s,如果建某条边,那么边权是 ,其中 和 是给定的值。求该图的最小生成树
分析
枚举每一条边 ,固定最大的边为 ,对于 跑最小生成树,不过有一些可以剪枝的地方是之前没有用上的边用上不会更优,所以说大大优化了时间复杂度。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define rr register
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
struct node{
int x,y,g,s,next;
bool operator <(const node &t)const{
if (g!=t.g) return g<t.g;
else return s<t.s;
}
}e[50001]; long long ws,wg,ans=1ll<<62;
int n,m,f[401],t,kr[50001],ls[401];//kr表示候选的编号(升序)
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline void kruskal(int mg){
rr bool v[t+1]; rr int cnt=1;
memset(v,0,sizeof(v));
for (rr int i=1;i<=n;++i) f[i]=i;
for (rr int i=1;i<=t;++i){
rr int tt=kr[i],fa=getf(e[tt].x),fb=getf(e[tt].y);
if (fa!=fb)
++cnt,v[i]=1,
f[min(fa,fb)]=max(fa,fb);
if (cnt==n){
rr int tot=0;
for (rr int j=1;j<=t;++j) if (v[j]) kr[++tot]=kr[j];//没有最小生成树那么肯定会被覆盖掉
fill(kr+1+tot,kr+1+t,0);
t=tot; ans=min(ans,wg*mg+ws*e[tt].s);//既然升序答案就是最后一条边
break;
}
}
}
signed main(){
n=iut(); m=iut(); wg=iut(); ws=iut();
for (rr int i=1;i<=m;++i){
rr int x=iut(),y=iut();
if (x==y) iut(),iut(),--i,--m;//处理自环
else e[i]=(node){x,y,iut(),iut(),ls[x]},ls[x]=i;
}
sort(e+1,e+1+m);
for (rr int i=1;i<=m;++i){
if (wg*e[i].g+ws*e[i].s>=ans) continue;//肯定不会更优
rr int l=1,r=++t;//二分插入排序
while (l<r){
rr int mid=(l+r)>>1;
if (e[kr[mid]].s>e[i].s) r=mid;
else l=mid+1;
}
if (l==t) kr[t]=i;
else{
for (rr int i=t;i>l;--i) kr[i]=kr[i-1];
kr[l]=i;
}
kruskal(e[i].g);
}
printf("%lld",ans);
return 0;
}
JZOJ 3518 进化序列
题目大意
有多少区间的按位或值小于
分析
扫描每一个结尾,然后找到最大的开头
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,m,a[100001],head=1,cnt[33]; long long ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
signed main(){
freopen("evolve.in","r",stdin);
freopen("evolve.out","w",stdout);
n=iut(); m=iut();
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1;i<=n;++i){
rr int sum=0;
for (rr int j=0;j<32;++j) sum|=(cnt[j]>0)<<j;
while ((sum|a[i])>=m&&head<=i){
for (rr int j=0;j<32;++j)
if (a[head]&(1<<j))
if (!(--cnt[j])) sum^=1<<j;
++head;
}
if (i>head) ans+=i-head;
for (rr int j=0;j<32;++j)
if (a[i]&(1<<j))
if (!cnt[j]++) sum|=1<<j;
}
printf("%lld",ans);
return 0;
}
JZOJ 5223 B
题目
给定一个3*3的网格图,一开始每个格子上都站着一个机器人。每一步机器人可以走到相邻格子或留在原地,同一个格子上可以有多个机器人。问走n步后,有多少种走法,满足每个格子上都有机器人。答案对10^9+7取模。
分析
然而这是一道矩阵乘法的题目,机器人只能跑到相邻的格子,于是跑完矩阵乘法后枚举全排列,对于每种全排列用乘法原理求出答案
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define rr register
using namespace std;
int b[9]; typedef long long ll;
const ll mod=1000000007ll;
ll n,a[9][9],ans;
inline void mulself(ll f[9][9]){
ll c[9][9];
memset(c,0,sizeof(c));
for (rr int i=0;i<9;++i)
for (rr int j=0;j<9;++j)
for (rr int k=0;k<9;++k)
(c[i][j]+=f[i][k]*f[k][j]%mod)%=mod;
memcpy(f,c,sizeof(c));
}
inline void mul(ll f[9][9],ll x){
ll c[9]={0,0,0,0,0,0,0,0,0};
for (rr int i=0;i<9;++i)
for (rr int j=0;j<9;++j)
(c[j]+=a[x][i]*f[i][j]%mod)%=mod;
memcpy(a[x],c,sizeof(c));
}
inline void init(ll x,ll y){
rr ll f[9][9]={//就是连相邻的边
{1,1,0,1,0,0,0,0,0},
{1,1,1,0,1,0,0,0,0},
{0,1,1,0,0,1,0,0,0},
{1,0,0,1,1,0,1,0,0},
{0,1,0,1,1,1,0,1,0},
{0,0,1,0,1,1,0,0,1},
{0,0,0,1,0,0,1,1,0},
{0,0,0,0,1,0,1,1,1},
{0,0,0,0,0,1,0,1,1}
};
for (;y;mulself(f),y>>=1) if (y&1) mul(f,x);
}
signed main(){
scanf("%lld",&n);
for (rr int i=0;i<9;++i)
a[i][i]=1,init(i,n),b[i]=i;
do{
rr ll mull=1;
for (rr int i=0;i<9;++i) (mull*=a[i][b[i]])%=mod;//乘法原理
(ans+=mull)%=mod;//加法原理
}while (next_permutation(b,b+9));//枚举全排列
printf("%lld",ans);
return 0;
}
后续
sto初二除了我以外的所有人