关于有很多好题写了但是不知道放哪所以就放在同一个博客里了md这标题怎么这么长这件事
不分难度.我只是想把一些好玩或者有思维含量的题塞进来.
无向图的最小环问题
\(n\leq100\),考虑\(Floyd\).
首先很好想,就是枚举一条\(i,j\)间的最短路,然后枚举一个中转点\(k\),求最小.
大概是这样:

浅绿色的是\(i,j\)间的最短路,浅蓝色的是\(i,k\)和\(k,j\)间的两条单独的边.这是显然正确的.
但是我们要注意一个问题:如果是先跑一边\(Floyd\),再找一遍最短路,是错的.理由显然:这样无法保证枚举的\(k\)不在这条\(i,j\)间的最短路上.所以要利用一下\(Floyd\)的特性(即在枚举到\(k\)之前\(k\)不会作为任意两点最短路径上的中转点)一边跑\(Floyd\),一边更新答案.
Code
#include<bits/stdc++.h>
namespace Lemuen_Daily
{
#define il inline
#define endl '\n'
#define ll long long
#define ldb long double
#define Croll(i,l,r) for(int i=l;i<=r;++i)
#define Troll(i,l,r) for(int i=l;i>=r;--i)
#define itn int
#define mian main
using std::cout;
const int maxn=1017;
const int inf=0x7f7f7f7f;
il ll read()
{
ll f=1,x=0;char w=getchar();
while(w<'0'||w>'9')
{if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9')
{x=(x<<3)+(x<<1)+(w^48);w=getchar();}
return f*x;
}
}using namespace Lemuen_Daily;
ll n,m;
ll ans;
ll mp[maxn][maxn];
ll dis[maxn][maxn];
ll min(ll a,ll b){return a>b?b:a;}
itn mian()
{
n=read();m=read();
Croll(i,1,n)
Croll(j,1,n)
{
if(i==j)continue;
mp[i][j]=dis[i][j]=inf;
}ans=inf;
Croll(i,1,m)
{
ll u=read(),v=read();
ll w=read();
dis[u][v]=dis[v][u]=min(dis[u][v],w);
mp[u][v]=mp[v][u]=min(mp[u][v],w);
}
// Croll(i,1,n)
// Croll(j,1,n)
// cout<<i<<" "<<j<<" "<<dis[i][j]<<endl;
Croll(k,1,n)
{
Croll(i,1,k-1)
Croll(j,i+1,k-1)
ans=min(ans,dis[i][j]+mp[i][k]+mp[k][j]);
Croll(i,1,n)
Croll(j,1,n)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
if(ans==inf)
cout<<"No solution."<<endl;
else
cout<<ans<<endl;
return 0;
}
HASH
乐子题.Trump昨天晚上roll出来的.要稍微动下脑子.
首先一眼爆搜.但是 \(26^{10}\) 显然过不去.考虑折半搜索.
左半边好说,从0开始搞就行了.
问题是右边咋弄:我们使其初始值为k,执行 该 \(Hash\) 运算法则 的逆运算.
然后想怎么搞这个逆运算.
我们设当前Hash值为 \(Ha\) ,当前运算后结果为 \(y\) .
则有
尝试交换运算顺序.易化简为
那显然,\(Ha\) 等于 \((y \oplus ord)\) 乘上 \(33 \mod 2^m\) 的逆元.
Code
#include<bits/stdc++.h>
namespace Lemuen_Daily
{
#define il inline
#define endl '\n'
#define ll long long
#define ldb long double
#define Croll(i,l,r) for(int i=l;i<=r;++i)
#define Troll(i,l,r) for(int i=l;i>=r;--i)
#define itn int
#define mian main
using std::cout;
const int maxn=1<<25;
const ll inf=0x7f7f7f7f7f;
il ll read()
{
ll f=1,x=0;char w=getchar();
while(w<'0'||w>'9')
{if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9')
{x=(x<<3)+(x<<1)+(w^48);w=getchar();}
return f*x;
}
}using namespace Lemuen_Daily;
int n,k,m;
int cnt[maxn];
ll ans,inv;
void dfs1(int c,int now)
{
if(c==n/2)
return cnt[now]++,void();
Croll(i,1,26)
dfs1(c+1,((now*33)^i)%m);
}
void dfs2(int c,int now)
{
if(c==0)
return ans+=cnt[now],void();
Croll(i,1,26)
dfs2(c-1,(now^i)*inv%m);
}
int mian()
{
n=read();k=read();m=1<<(read());
Croll(i,1,m-1)
if(i*33%m==1)inv=i;
dfs1(0,0);dfs2((n+1)/2,k);
cout<<ans<<endl;
return 0;
}
Inversion Counting
我是不会告诉你其实我是因为这题标签有平衡树才做的.
这题标签有平衡树.但是并不需要平衡树.这题操作时在反转区间嘛,然后求逆序对.
容易发现,实际上有变化的逆序对只有完全在这个区间内的.换句话说,反转操作对该区间外的答案无影响.
那么这个区间内的逆序对个数是怎么变得捏?显然,原来的逆序对都会变为正序的;原来的正序对都会变为逆序的.但是顺着这条路想的话好像最终还是会跑到求区间逆序对上.那这题就没意思了.
所以换种思路.答案只要求输出奇偶,所以从奇偶性方面考虑.那容易发现区间原正序对个数奇偶性等于现在逆序对个数的奇偶性.设区间长度为 \(len\) ,则区间内数对总数为 \(\frac{len*(len-1)}{2}\) .再设反转后区间逆序对个数为 \(num\) ,则现区间内正序对数量为 \(num\) .则现区间内逆序对数量为 \(\frac{len*(len-1)}{2}-num\) .
然后捏,大的来了:
反转前逆序对数量 减去 反转前后逆序对数量差 就是 反转后逆序对数量.
所以捏,想判断 反转后逆序对数量的奇偶性,只需判断 反转前后逆序对数量差 的奇偶性.
那这个数量差很显然是 \(\frac{len*(len-1)}{2}-2num\) . \(2num\) 是偶数(显然),那么我们现在只需要判断 \(\frac{len*(len-1)}{2}\) 的奇偶性了.这个是平凡的.
Code
#include<bits/stdc++.h>
namespace Lemuen_Daily
{
#define endl '\n'
#define il inline
#define ll long long
#define ldb long double
#define Croll(i,l,r) for(int i=l;i<=r;++i)
#define Troll(i,l,r) for(int i=l;i>=r;--i)
#define cerr_time std::cerr<<(double)clock()/CLOCKS_PER_SEC
const int maxn=101700;
const ll inf=0x7f7f7f7f7f;
il int read()
{
int f=1,x=0;char w=getchar();
while(w<'0'||w>'9')
{if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9')
{x=(x<<3)+(x<<1)+(w^48);w=getchar();}
return f*x;
}
}using namespace Lemuen_Daily;
int n,m;
int w[maxn];
bool flag;
int num;
namespace Lemuen_BIT
{
int tr[maxn];
int lowbit(int x)
{return x&(-x);}
void add(int x)
{
while(x<=n)
{
tr[x]++;
x+=lowbit(x);
}
}
int query(int x)
{
int ans=0;
while(x)
{
ans+=tr[x];
x-=lowbit(x);
}
return ans;
}
void Get()
{
Troll(i,n,1)
{
num+=query(w[i]);
add(w[i]);
}
}
}
int main()
{
n=read();
Croll(i,1,n)
w[i]=read();
Lemuen_BIT::Get();
flag=!(num%2);
m=read();
while(m--)
{
int l=read(),r=read();
int p=r-l+1;
if((p*(p-1)/2)%2)
flag=!flag;
if(flag)
std::cout<<"even"<<endl;
else
std::cout<<"odd"<<endl;
}
return 0;
}
集合 First
《关于我最后一个小时才想起来有场比赛没打,最后二十分钟想这道题但是没想出来,然后比赛结束不到十分钟想出来了这回事》
乐子题.
数据范围到 \(1e16\),\(O(n)\) 都不用想.试着想想能否单独计算每个数的贡献.
首先我们容易得出一个结论:因为从大到小排序,所以一个数的排名只与"大于他的数的个数"有关.
因为最后一个数没有大于他的数,所以先考虑前 \(n-1\) 个数.那么对于每个 \(x\),大于它的数会有 \(n-x\) 个.(后文用 \(m\) 替代 \(n-x\)).这 \(m\) 个数,能组成的集合个数为 \(2^{m}\) 个.因为要求x的排名,所以考虑这 \(m\) 个数组成的集合的大小.易得,有 \(2^{m-1}\) 个集合大小为奇数,剩下 \(2^{m-1}\) 个的大小为偶数.
所以我们就能得到一个很抽象但是正确的结论:
这 \(n-1\) 个数,贡献均为 \(0\).
逆天吧?我也觉得逆天.
然后再来单独考虑最后一个数.我们容易发现,无论怎么组合,他都始终排在第一位,也就是说,他只会产生正贡献.
那么它会产生多少次贡献呢?因为前 \(n-1\) 个数能组成 \(2^{n-1}\) 个集合,所以为 \(2^{n-1}\) 次.
所以,我们得出总结论:
答案为 \(n \times 2^{n-1}\).
Code
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define endl '\n'
#define int long long
const int mod=911451407;
#define Croll(i,l,r) for(int i=l;i<=r;++i)
#define Troll(i,l,r) for(int i=l;i>=r;--i)
//////////
int n;
int qp(int,int);
//////////
il int read();
//////////
signed main()
{
n=read();
int ans=n%mod*qp(2,n-1)%mod;
cout<<ans<<endl;
return 0;
}
//////////
int qp(int x,int k)
{
int res=1;
while(k)
{
if(k & 1)res=res*x%mod;
x=x*x%mod;k>>=1;
}
return res;
}
//////////
il int read()
{
int f=1,x=0;char w=getchar();
while(w<'0'||w>'9')
{if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9')
{x=(x<<3)+(x<<1)+(w^48);w=getchar();}
return f*x;
}

你说的对,但是前面忘了,中间忘了,命运二最好的时代来辣!
浙公网安备 33010602011771号