noip模拟62[好势头]
noip模拟62 solutions
哈哈,这应该是我第一次\(rank1\)吧,还是个并列的,没事好势头
所以前两个题确实水,所以这就是我后两个题改了一上午+一晚上的理由???
T1 Set
这个只要找到一个规律就是这些数里面一定会有一段连续的可以使和被整除
证明:首先这里一共有\(1e6\)个数,在\(\mod 1e6\)的条件下只有值域在\([1,999999]\)之间的才合法
因为如果是\(0\)的话,就出现整除了
所以我们不断累加这个序列,会出现\(1e6\)个数,所以在值域上一定会有重复
重复意味着什么?说明从上一次出现到这一次出现的加和会被整除!!!
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
const int M=2e5+5;
int n,m,ans;
int to[M*2],nxt[M*2],head[N],rp=1;
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
int dep[N],cir;
int ble[M*2];
int vis[M*2],val[N];
void dfs(int x,int d){
dep[x]=d;
for(int i=head[x];i;i=nxt[i]){
if(vis[i])continue;
vis[i]=vis[i^1]=true;
int y=to[i];
if(dep[y]){
int tmp=dep[x]-dep[y]+1;
if(tmp&1){
ble[i]++;ble[i^1]++;
val[y]--;cir++;
}
else {
ble[i]--;ble[i^1]--;
val[y]++;
}
val[x]+=ble[i];
continue;
}
dfs(y,d+1);
ble[i]=ble[i^1]=val[y];
val[x]+=val[y];
}
}
signed main(){
#ifdef oj
freopen("a,in","r",stdin);
freopen("a.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
fo(i,1,m){
int x,y;scanf("%d%d",&x,&y);
add_edg(x,y);add_edg(y,x);
}
dfs(1,1);
for(int i=2;i<=rp;i+=2)if(ble[i]==cir)ans++;
printf("%d",ans);
}
T2 Read
所以这个还是我的算法垃圾了???
题解确实高明,利用最大的一定会多于一半这个性质,用了两个数就解决了
我不会这个,所以我就开桶来记录个数,最后遍历桶来找到最多的
你说桶开不下,值域是\(1e9\)
那我分两次统计行不行,先\(1e5\) \(1e5\)的统计,就是说我将某\(1e5\)个数当成一个数
放在同一个桶里,这样可以保证最大的数一定在最大的桶里,因为最大的数要大于一半
所以我们找到最大的桶之后就可以直接再扫一遍,统计当前桶中的值的个数
这次值域只有\(1e5\)了吧
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
int n,now,M,K,cunt[1005],X[1005],Y[1005],Z[1005];
int sum[N],lim=1e5;
signed main(){
#ifdef oj
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
//cout<<(sizeof(sum)>>20)<<endl;
//cout<<((1<<30)/lim+1)<<endl;
scanf("%lld%lld",&M,&K);
fo(i,1,M)scanf("%lld",&cunt[i]);
fo(i,1,M)scanf("%lld",&X[i]);
fo(i,1,M)scanf("%lld",&Y[i]);
fo(i,1,M)scanf("%lld",&Z[i]);
int S=(1<<K)-1;
fo(i,1,M){
n++;now=X[i];
int last=X[i];
//cout<<now<<" ";
sum[now/lim+1]++;
fo(j,1,cunt[i]-1){
last=(last*Y[i]+Z[i])&S;
n++;now=last;//cout<<now<<" ";
sum[now/lim+1]++;
}
}
int mx=0,id=0;
fo(i,1,lim)if(sum[i]>mx)id=i,mx=sum[i];
if(mx<=n/2){printf("0");return 0;}
memset(sum,0,sizeof(sum));mx=n=0;
int l=(id-1)*lim,r=id*lim;
fo(i,1,M){
n++;now=X[i];
int last=X[i];
if(now>=l&&now<r)sum[now-l]++;
fo(j,1,cunt[i]-1){
last=(last*Y[i]+Z[i])&S;
n++;now=last;
if(now>=l&&now<r)sum[now-l]++;
}
}
fo(i,0,r-l-1)if(sum[i]>mx)mx=sum[i];
if(mx>n-mx)printf("%lld",mx-n+mx-1);
else printf("0");
}
T3 题目交流通道
这个嘛,我调了\(3h\)。
异常艰辛,首先,我们将距离为0的点缩成一个点,所以我们会出现重边,这样会有一部分方案数
然后考虑距离为0的点内部的方案数,设\(f[i]\)表示距离为0的i个点的方案,\(g[i]\)表示任意距离的i个点的方案
这个表示,我当前图中的点任意连边,边权任意的方案数
首先我保证1节点一定在合法的集合内,这样可以保证无论如何都不会算重
因为如果可能不合法的那边包含合法的部分,比如说有两个节点合法,那他们不包含1
就不会被我放到要减去的方案中,,所以我保证了不会算重。。。
上面那个式子就是一个容斥用所有方案减去不合法的方案,分成两个集合然后两个集合的连边全部不合法
这个容斥好像挺常见的,所以还是记住吧
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=405;
const int mod=998244353;
int n,m,lim[N][N],ans=1;
int fa[N],siz[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return ;
fa[fy]=fx;siz[fx]+=siz[fy];
}
struct node{int val,sum,fl;}edg[N][N];
bool vis[N][N],via[N],fla[N][N];
int ksm(int x,int y){
int ret=1;x%=mod;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int jc[N],inv[N];
int C(int x,int y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
int f[N],g[N];
signed main(){
#ifdef oj
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
scanf("%lld%lld",&n,&m);bool flag=false;//m%=mod;
fo(i,1,n)fa[i]=i,siz[i]=1;
fo(i,1,n)fo(j,1,n)scanf("%lld",&lim[i][j]);
fo(i,1,n)if(lim[i][i])flag=true;
fo(i,1,n)fo(j,i+1,n)if(lim[i][j]!=lim[j][i]||lim[i][j]>m)flag=true;
fo(k,1,n)fo(i,1,n)fo(j,1,n)if(lim[i][j]>lim[i][k]+lim[k][j])flag=true;
if(flag){printf("0");return 0;}
fo(i,1,n)fo(j,1,n)if(i!=j&&!lim[i][j])merge(i,j);
fo(k,1,n){
if(find(k)!=k)continue;
fo(i,1,n){
if(find(i)!=i)continue;
fo(j,1,n){
if(find(j)!=j)continue;
if(i==j||j==k||i==k)continue;
if(lim[i][j]==lim[i][k]+lim[k][j])edg[i][j].fl=true;
}
}
}
//cout<<ans<<endl;
fo(i,1,n)fo(j,1,n)if(lim[i][j])edg[find(i)][find(j)].sum++;
fo(i,1,n)fo(j,i+1,n){
//if(i==j)continue;
int fx=find(i),fy=find(j);
if(fx!=i||fy!=j)continue;
int tmp=edg[fx][fy].sum;
if(edg[fx][fy].fl)ans=ans*ksm(m-lim[i][j]+1,tmp)%mod;//cout<<edg[fx][fy].sum<<endl;
else ans=ans*(ksm(m-lim[i][j]+1,tmp)-ksm(m-lim[i][j],tmp)+mod)%mod;//cout<<edg[fx][fy].sum<<endl;
}
jc[0]=1;fo(i,1,n)jc[i]=jc[i-1]*i%mod;
inv[0]=1;inv[n]=ksm(jc[n],mod-2);
fu(i,n-1,1)inv[i]=inv[i+1]*(i+1)%mod;
fo(i,1,n)g[i]=ksm(m+1,i*(i-1)/2);
fo(i,1,n){
f[i]=g[i];
fo(j,1,i-1){
int tmp=f[j]*g[i-j]%mod*C(i-1,j-1)%mod*ksm(m,j*(i-j))%mod;
f[i]=(f[i]+mod-tmp)%mod;
}
//cout<<f[i]<<" "<<g[i]<<endl;
}
//cout<<ans<<endl;
fo(i,1,n){
int fx=find(i);
if(via[fx])continue;
//cout<<siz[fx]<<" "<<f[1]<<endl;
ans=ans*f[siz[fx]]%mod;
via[fx]=true;
}
printf("%lld",ans);
}
T4 题目难度提升
所以这个题是整天骂我讲题讲不好的人第一个切的——————AaMuXiiiiii
就是这个人,整天踩我博客,整天骂我不会讲题
然后我去不耻下问了。。。。。
就是首先看中位数有两个及以上的时候,就直接上上下下的放就好了
然后如果不重复,那就让中位数小于当前所有还没加入的数
就直接分类讨论,然后卡一卡你的\(lower_bound\)和\(upper_bound\)就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
multiset<int> outer,inner;
int n,a[N],mid;
void push(int x){
printf("%d ",x);
outer.insert(x);
inner.erase(x);
}
signed main(){
#ifdef oj
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
#endif
scanf("%d",&n);
fo(i,1,n)scanf("%d",&a[i]),inner.insert(a[i]);
sort(a+1,a+n+1);
mid=(1+n)/2;
if (a[mid]==a[mid+1]){
for (;a[mid]==a[mid+1];mid++);
printf("%d",a[mid]);
int p=mid-1,q=n;
for (;p||q>mid;){
if (p) printf(" %d",a[p--]);
if (q>mid) printf(" %d",a[q--]);
}
return 0;
}
mid=*inner.begin();
//printf("%d ",*inner.begin());
push(*inner.begin());
for(int i=2;i<n;i+=2){
auto in=inner.begin();
auto le=outer.lower_bound(mid);
auto ri=--outer.lower_bound(*in);
if(*le>=*ri){
int now=2*(*in)-mid;//cout<<*in<<" "<<now<<endl;
auto on=--inner.upper_bound(now);
ri=--outer.upper_bound(now);
if(*le<*ri)push(*inner.rbegin());
//printf("%d ",*on);
else push(*on);
}
else {
auto on=inner.rbegin();
//printf("%d ",*on);
push(*on);
}
double md=1.0*(mid+(*++outer.lower_bound(mid)))/2.0;
int tmd=md+0.5;//printf("%d %d ",mid,tmd);
in=inner.begin();
le=outer.lower_bound(tmd);//cout<<*le<<" ";
ri=--outer.lower_bound(*in);//cout<<*ri<<endl;
if(*le>*ri){
//printf("%d ",*in);
push(*in);
}
else {
//printf("%d ",*inner.rbegin());
push(*inner.rbegin());
}
mid=*++outer.lower_bound(mid);
//cout<<endl;
}
if(!(n&1))printf("%d ",*inner.begin());
}