模拟赛总结合集
20231023: NOIP2023-div2模拟赛24
A. 公园
题意
给一个无向图\(G=(V,E)\), 求一个\(X\), 使得\(D\times X+\sum\limits_{e\in G,dist(1,e.u)> X or dist(1,e.v)> X}cost(e)\)最小, 求最小值
赛时思路
非常的简单啊
算法概述
处理出一号点到所有点的最短路, 按最短路从大到小排序依次加入集合, 加入集合的时候把对应的连边也加入, 然后这时的答案就是最短路乘上\(D\)再加上边集的权值和
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e18;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=100000;
const int MxM=200000;
int n,m,cost;
int head[MxN+5],nxt[2*MxM+5],to[2*MxM+5],tot;
int flip[2*MxM+5],length[2*MxM+5],dist[MxN+5];
int rank[MxN+5],vis[2*MxM+5];
struct node{
int index;
int distance;
friend bool operator<(node U,node V){
return U.distance>V.distance;
}
};priority_queue<node>Q;
bool cmp1(int U,int V){
return dist[U]<dist[V];
}
int AddEdge(int u,int v,int w){
to[++tot]=v;
length[tot]=w;
nxt[tot]=head[u];
head[u]=tot;
return tot;
}
void Dijkstra(){
for(int i=1;i<=n;i++){
dist[i]=inf;
}
dist[1]=0;
Q.push({1,0});
while(!Q.empty()){
int p=Q.top().index;
Q.pop();
for(int i=head[p];i;i=nxt[i]){
if( dist[to[i]]>dist[p]+length[i]){
dist[to[i]]=dist[p]+length[i];
Q.push({to[i],dist[to[i]]});
}
}
}
}
void Main(int Case){
int u,v,w;
scanf("%lld%lld%lld",&n,&m,&cost);
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&w);
int p=AddEdge(u,v,w);
int q=AddEdge(v,u,w);
flip[p]=q;flip[q]=p;
}
Dijkstra();
for(int i=1;i<=n;i++){
rank[i]=i;
}
sort(rank+1,rank+n+1,cmp1);
int ans=inf,cur=0;
for(int i=n;i>=0;i--){
ans=min(ans,cur+cost*dist[rank[i]]);
if(!i)break;
for(int j=head[rank[i]];j;j=nxt[j]){
if(!vis[j]&&!vis[flip[j]]){
vis[j]=1;cur+=length[j];
}
}
}
printf("%lld\n",ans);
}
void Initialize(){
}
}
char InputFilename[105]="park.in";
char OutputFilename[105]="park.out";
signed main(){
freopen(InputFilename,"r",stdin);
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
B. 括号
题意
给一个长度为\(n\)的序列, 奇数位置的意义是有\(c_i\)个左括号, 偶数位置的意义是有\(c_i\)个右括号
求这个字符串的匹配子串个数.
赛时思路
想了一堆奇怪的假做法之后......
考虑把整个字符串想象成一个图像, 出现正解
算法概述
维护一个栈, 这是一个单调栈, 栈中元素是波谷, 它的高度是单调递增的, 对于每个点记录它之前与它高度相同的谷的数量, 然后加入点的时候, 取出栈中比它高的点, 加起来放进答案即可
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e18;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=10000000;
int n;
uint ans;
int c[MxN+5];
int stk[MxN+5],top;
int depth[MxN+5];
int dp[MxN+5];
void Main(int Case){
int x,y,z,m[2];
cin>>n;
for(int i=1;i<=n;i++)cin>>c[i];
int prefix=0,minn=0;top=0;ans=0;
for(int i=1;i<=n;i+=2){
prefix+=c[i];
ans=ans+prefix-max(minn,prefix-c[i+1]);
prefix-=c[i+1];
depth[i]=prefix;
while(top&&depth[stk[top]]>depth[i]){
ans+=dp[stk[top--]];
}
int u=0;
if(top&&depth[stk[top]]==depth[i]){
ans+=dp[stk[top]];
u=stk[top--];
}
dp[i]=dp[u]+(prefix>=minn);
stk[++top]=i;
minn=min(minn,prefix);
}
printf("%llu\n",ans);
}
void Initialize(){
}
}
char InputFilename[105]="brackets.in";
char OutputFilename[105]="brackets.out";
signed main(){
freopen(InputFilename,"r",stdin);
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
C. 学校
题意
给一个序列, 求它有多少个非空子序列满足任意相邻的四个数异或和不为S
赛时思路
暴力, 走人
算法概述
\(dp_i\)表示以第i个元素结尾的方案数.
\(sum_{i,j}\)表示前i个元素异或和为j的方案数.
枚举上一个点取哪个点,转移过来即可.
注意减去不合法的方案数.
简单的代码
/*
* Contest:
* Problem:
* Date:
* 我为什么赛时没有想到啊啊啊啊啊啊
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=998244353;
const int inf=1e18;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=5000;
int n,m,s,ans;
int Arr[MxN+5];
int dp[MxN+5];
int sum[MxN+5][MxN+5];
void Main(int Case){
scanf("%lld%lld%lld",&n,&m,&s);
for(int i=1;i<=n;i++){
scanf("%lld",&Arr[i]);
}
for(int i=1;i<=n;i++){
dp[i]=1;
for(int j=0;j<=MxN;j++){
sum[i][j]=sum[i-1][j];
}
for(int j=1;j<i;j++){
dp[i]=(dp[i]+dp[j]-sum[j-1][Arr[i]^Arr[j]^s]+MOD)%MOD;
sum[i][Arr[i]^Arr[j]]=(sum[i][Arr[i]^Arr[j]]+dp[j]-sum[j-1][Arr[i]^Arr[j]^s]+MOD)%MOD;
}
ans=(ans+dp[i])%MOD;
}
printf("%lld\n",ans);
}
void Initialize(){
}
}
char InputFilename[105]="school.in";
char OutputFilename[105]="school.out";
signed main(){
// freopen(InputFilename,"r",stdin);
// freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
D. 运算
题意
有常量d,n,每次读入一个正整数x.问最小的非负整数k使得\(xd^k\)是n的倍数.
但是李华机有问题,每次试图修改变量的时候会加上一个在\([L,R]\)中等概率随机的整数.
具体地:
当你读入\(x\)时,设你读入的数为\(x_0\),则实际的结果是\(x=x0+t(L≤t≤R)\)
每次做乘法时会将\(x\)修改成\(xd+t(L≤t≤R)\)。
赛时思路
考虑同余最短路, 使用线段树优化建图, 挂掉
算法概述
直接开始BFS求最短路, 发现被松弛过的点无需再次遍历, 也无需建边, 考虑使用并查集把遍历过的点缩掉, 时间复杂度 \(O(Tn\alpha(n))\)
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
// #define int long long
// #define uint unsigned long long
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e9;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=2000000;
int n;
int head[MxN+5],nxt[2*MxN+5],to[2*MxN+5],tot;
int Queue[MxN+5],Head,Tail;
int dist[MxN+5];
void AddEdge(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
struct Union_Find_Set{
int father[MxN+5];
int Find(int u){
if(father[u]==u)return u;
return father[u]=Find(father[u]);
}
void Union(int u,int v){
int fu=Find(u),fv=Find(v);
father[fu]=fv;
}
void Clear(){
for(int i=0;i<=n;i++)father[i]=i;
}
}S;
void Main(int Case){
int d,L,R,q,u;
scanf("%d%d%d%d%d",&n,&d,&L,&R,&q);
for(int i=0;i<=n;i++){
dist[i]=-1;
head[i]=0;
}
tot=0;
S.Clear();
for(int i=0;i<n;i++){
AddEdge((integer)i*d%n,i);
}
Head=1;Tail=0;
for(int cur=S.Find(n-R);cur<=n-L;cur=S.Find(cur)){
dist[cur]=0;
Queue[++Tail]=cur;
S.Union(cur,cur+1);
}
while(Head<=Tail){
int p=Queue[Head++];
for(int i=head[p];i;i=nxt[i]){
int lbound=((integer)(to[i]-R)%n+n)%n;
int rbound=((integer)(to[i]-L)%n+n)%n;
if(lbound<=rbound){
for(int cur=S.Find(lbound);cur<=rbound;cur=S.Find(cur)){
dist[cur]=dist[p]+1;
Queue[++Tail]=cur;
S.Union(cur,cur+1);
}
}
else{
for(int cur=S.Find(0);cur<=rbound;cur=S.Find(cur)){
dist[cur]=dist[p]+1;
Queue[++Tail]=cur;
S.Union(cur,cur+1);
}
for(int cur=S.Find(lbound);cur<n;cur=S.Find(cur)){
dist[cur]=dist[p]+1;
Queue[++Tail]=cur;
S.Union(cur,cur+1);
}
}
}
}
for(int i=1;i<=q;i++){
scanf("%d",&u);
printf("%d\n",dist[u]);
}
}
void Initialize(){
}
}
char InputFilename[105]="calculator.in";
char OutputFilename[105]="calculator.out";
signed main(){
freopen(InputFilename,"r",stdin);
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
20231024: NOIP2023-div2模拟赛25
A. 勇者
题意
有二维数组\(A\).
求\(\sum\limits^{}_{i,j,k,l,i\leq k,j\leq l}[A_{i,j}=L\wedge A_{i,l}=O \wedge A_{k,j}=I]\)
赛时思路
非常的简单啊
算法概述
前缀和解决.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const integer inf=1e18;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=3000;
int n,m;
char G[MxN+5][MxN+5];
int SI[MxN+5][MxN+5];
int SO[MxN+5][MxN+5];
void Main(int Case){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",G[i]+1);
}
integer ans=0;
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
SI[i][j]=SI[i+1][j]+(G[i][j]=='I');
SO[i][j]=SO[i][j+1]+(G[i][j]=='O');
if(G[i][j]=='J'){
ans=ans+1ll*SI[i][j]*SO[i][j];
}
}
}
printf("%lld\n",ans);
}
void Initialize(){
}
}
char InputFilename[105]="brave.in" ;
char OutputFilename[105]="brave.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
B. K 近查询
题意
查询第k近的比i大的数.
赛时思路
使用set瞎搞,时间复杂度假了.
算法概述
使用插入排序和链表,可以证明均摊复杂度\(O(nm)\)
简单的代码
/*
* Contest:
* Problem:
* Date:
*
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e9;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace FastIO{
const int bufl=1<<15;
char buf[bufl],*s=buf,*t=buf;
inline int _fetch(){
if(s==t){t=(s=buf)+fread(buf,1,bufl,stdin);if(s==t)return EOF;}
return *s++;
}
inline int Read(){
int s=0;char ch=_fetch(),last;
while(ch<'0'||ch>'9')last=ch,ch=_fetch();
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=_fetch();
return last=='-'?-s:s;
}
void Read(int &x){
char ch=_fetch(),last;
while(ch<'0'||ch>'9')last=ch,ch=_fetch();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=_fetch();
if(last=='-')x=-x;
}
void Write(int x){
if(x<0){putchar('-');x=-x;}
if(x>=10){Write(x/10);}putchar(x%10+'0');
}
void Endl(){putchar('\n');}
}
namespace WangManhe{
using namespace FastIO;
const int Mxn=10000000;
const int MxN=3000000;
int n,lim,type;
int Arr[Mxn+5];
int Answer[Mxn+5][6];
int nxt[Mxn+5],lst[Mxn+5];
int tag[Mxn+5];
void Main(int Case){
Read(n);Read(lim);Read(type);
for(int i=1;i<=n;i++)lst[i]=i-1,nxt[i]=i+1;
lst[n+1]=n;nxt[0]=1;
for(int i=1;i<=n;i++){
Read(Arr[i]);
}
for(int i=1;i<=n;i++){
for(int j=1,p=lst[i];j<=lim;j++){
while(p!=0&&Arr[p]<Arr[i]){
if(++tag[p]==lim){
lst[nxt[p]]=lst[p];
nxt[lst[p]]=lst[p];
}
p=lst[p];
}
Answer[i][j]=p;
p=lst[p];
}
}
if(type==0){
for(int i=1;i<=n;i++){
for(int j=1;j<=lim;j++){
printf("%d ",Answer[i][j]);
}
printf("\n");
}
}
else{
integer answer=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=lim;j++){
if(!Answer[i][j])break;
answer=answer^(1ll*Answer[i][j]*i*j);
}
}
printf("%lld\n",answer);
}
}
void Initialize(){
}
}
char InputFilename[105]="kth.in" ;
char OutputFilename[105]="kth.out";
signed main(){
// freopen( InputFilename,"r",stdin );
// freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
C. 树上流水
题意
给定一棵 n 个点的有根外向树,根为 1 ,每条边有容量。
初始在根处有无限的水,其他位置没有水。每秒可以把每个点的水往儿子推,但推的量不能超过这个点原有的水量,也不能超过边的容量。
可以向多个儿子推水,只要总和没有超过限制就行。
推的过程是同时的,所以不能有水在一秒内走两条边。
给定 K,求最小几秒之后可以使得叶子处的总水量至少为 K 。
赛时思路
按深度贪心.
没开int128
算法概述
二分然后按深度贪心
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int Integer
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e18;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace FastIO{
const int bufl=1<<15;
char buf[bufl],*s=buf,*t=buf;
inline int _fetch(){
if(s==t){t=(s=buf)+fread(buf,1,bufl,stdin);if(s==t)return EOF;}
return *s++;
}
inline int Read(){
int s=0;char ch=_fetch(),last;
while(ch<'0'||ch>'9')last=ch,ch=_fetch();
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=_fetch();
return last=='-'?-s:s;
}
void Read(int &x){
char ch=_fetch(),last;
while(ch<'0'||ch>'9')last=ch,ch=_fetch();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=_fetch();
if(last=='-')x=-x;
}
void Write(int x){
if(x<0){putchar('-');x=-x;}
if(x>=10){Write(x/10);}putchar(x%10+'0');
}
void Endl(){putchar('\n');}
}
namespace WangManhe{
using namespace FastIO;
const int MxN=50000;
int n,m;
int load[MxN+5];
int res[MxN+5];
int head[MxN+5],nxt[2*MxN+5],to[2*MxN+5],tot;
int siz[MxN+5],son[MxN+5],father[MxN+5],depth[MxN+5],top[MxN+5];
int L[MxN+5],R[MxN+5],rnk[MxN+5],tim;
vector<int>LeaF;
bool Comp(int U,int V){
return depth[U]<depth[V];
}
void AddEdge(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
void DFS1(int p,int fa){
siz[p]=1;
depth[p]=depth[fa]+1;
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa){
DFS1(to[i],p);
siz[p]+=siz[to[i]];
if(siz[to[i]]>siz[son[p]]){
son[p]=to[i];
}
}
}
if(siz[p]==1){
LeaF.push_back(p);
}
}
void DFS2(int p,int fa,int tp){
L[p]=++tim;
rnk[tim]=p;
top[p]=tp;
if(son[p]){
DFS2(son[p],p,tp);
}
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa&&to[i]!=son[p]){
DFS2(to[i],p,to[i]);
}
}
R[p]=tim;
}
struct Segment_Tree{
int value[4*MxN+5];
int tag[4*MxN+5];
void PushUp(int p){
value[p]=min(value[p<<1],value[p<<1|1]);
}
void PushDown(int p){
if(tag[p]){
value[p<<1]+=tag[p];
value[p<<1|1]+=tag[p];
tag[p<<1]+=tag[p];
tag[p<<1|1]+=tag[p];
tag[p]=0;
}
}
void Modify(int L,int R,int l,int r,int c,int p){
// cout<<"-> "<<L<<" "<<R<<" "<<l<<" "<<r<<endl;
if(L==l&&R==r){
tag[p]+=c;
value[p]+=c;
return;
}
PushDown(p);
int mid=(L+R)>>1;
if(r<=mid){
Modify(L,mid,l,r,c,p<<1);
}
else if(l>mid){
Modify(mid+1,R,l,r,c,p<<1|1);
}
else{
Modify(L,mid,l,mid,c,p<<1);
Modify(mid+1,R,mid+1,r,c,p<<1|1);
}
PushUp(p);
}
int Query(int L,int R,int l,int r,int p){
// cout<<"--> "<<L<<" "<<R<<" "<<l<<" "<<r<<endl;
if(L==l&&R==r){
return value[p];
}
PushDown(p);
int mid=(L+R)>>1;
if(r<=mid){
return Query(L,mid,l,r,p<<1);
}
else if(l>mid){
return Query(mid+1,R,l,r,p<<1|1);
}
else{
return min(Query(L,mid,l,mid,p<<1),Query(mid+1,R,mid+1,r,p<<1|1));
}
}
}Sgt;
int QueryMin(int u,int v){
int res=inf;
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]){
swap(u,v);
}
res=min(res,Sgt.Query(1,n,L[top[u]],L[u],1));
u=father[top[u]];
}
if(depth[u]>depth[v]){
swap(u,v);
}
if(u==1&&v!=1) res=min(res,Sgt.Query(1,n,L[u]+1,L[v],1));
else if(u!=1) res=min(res,Sgt.Query(1,n,L[u],L[v],1));
return res;
}
void Modify(int u,int v,int c){
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]){
swap(u,v);
}
Sgt.Modify(1,n,L[top[u]],L[u],c,1);
u=father[top[u]];
}
if(depth[u]>depth[v]){
swap(u,v);
}
if(u==1&&v!=1) Sgt.Modify(1,n,L[u]+1,L[v],c,1);
else if(u!=1) Sgt.Modify(1,n,L[u],L[v],c,1);
}
bool Check(int x){
for(auto u:LeaF){
res[u]=0;
}
int ans=0;
// cout<<x<<endl;
for(auto u:LeaF){
res[u]=QueryMin(1,u);
ans=ans+res[u]*max((Integer)0,x-depth[u]+2);
Modify(1,u,-res[u]);
// cout<<u<<" "<<res[u]<<endl;
}
for(auto u:LeaF){
Modify(1,u,res[u]);
}
return ans>=m;
}
void Main(int Case){
int u,v;
n=Read();m=Read();
tot=0;tim=0;
for(int i=1;i<=n;i++){
head[i]=0;
son[i]=0;
}
for(int i=2;i<=n;i++){
father[i]=Read();
AddEdge(father[i],i);
AddEdge(i,father[i]);
}
for(int i=2;i<=n;i++){
load[i]=Read();
}
LeaF.clear();
DFS1(1,0);
DFS2(1,0,1);
for(int i=2;i<=n;i++){
Sgt.Modify(1,n,L[i],L[i],load[i],1);
}
sort(LeaF.begin(),LeaF.end(),Comp);
int l=0,r=1e9,mid,res;
while(l<=r){
mid=(l+r)>>1;
if(Check(mid)){
res=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
for(int i=2;i<=n;i++){
Sgt.Modify(1,n,L[i],L[i],-load[i],1);
}
Write(res);
Endl();
}
void Initialize(){
}
}
char InputFilename[105]="tree.in" ;
char OutputFilename[105]="tree.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;Case++){
WangManhe::Main(Case);
}
return 0;
}
D. 逆序对
题意
给定一长为 2
n 的序列 p1, p2, · · · p2n,m 次操作,
每次操作给出三个整数 qi, li, ri,
然后对序列作如下变换:
• 将序列分为 \(2^{q_i}\) 个大小为 \(2^{n−q_i}\) 的块。
• 选择第 \(l_i\) 到第 \(r_i\) 个块。
• 对于每个选择的块,将其翻转。
每次操作后,输出整个序列的逆序对数。
赛时思路
暴力走人
算法概述
考虑这是个分治结构, 用类似懒标记的思想,记录这个节点的区间内, 第i层被翻转过了的情况的值, 然后就可以算了.
赛时就差一点点
简单的代码
有点长
20231026: NOIP2023-div2模拟赛26
A. 发射器
题意
给平面上一个点集. 求一个最小的X使得存在一个连通子图满足图中的边均小于2X.
赛时思路
非常的简单啊,直接二分
算法概述
明明可以MST
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const integer inf=1e18;
const ldb eps=1e-12;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=1000;
int n;
struct Point{
int x,y;
}P[MxN+5];
ldb dist[MxN+5][MxN+5];
struct DSU{
int father[MxN+5];
void Clear(){
for(int i=1;i<=n;i++){
father[i]=i;
}
}
int Find(int u){
if(father[u]==u){
return u;
}
return father[u]=Find(father[u]);
}
void Union(int u,int v){
int fu=Find(u),fv=Find(v);
if(fu!=fv)
father[fu]=fv;
}
}dsu;
bool Check(ldb mid){
dsu.Clear();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(dist[i][j]<=4*mid*mid){
dsu.Union(i,j);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(dsu.Find(i)!=dsu.Find(j)){
return 0;
}
}
}
return 1;
}
void Main(int Case){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&P[i].x,&P[i].y);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dist[i][j]=1.0L*(P[i].x-P[j].x)*(P[i].x-P[j].x)+1.0L*(P[i].y-P[j].y)*(P[i].y-P[j].y);
}
}
ldb l=0,r=1000000000,res;
while(r-l>=eps){
ldb mid=(l+r)/2;
if(Check(mid)){
r=mid;
res=mid;
}
else{
l=mid;
}
}
printf("%.12Lf\n",res);
}
void Initialize(){
}
}
char InputFilename[105]="launcher.in" ;
char OutputFilename[105]="launcher.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
B. 树上删边
题意
有一棵n个结点的树,每个结点有一个权值,删除一条边的费用为该边连接子树中结点权值最大值之和.问以任意顺序删除树中所有边的最小花费.
赛时思路
贪心即可
算法概述
贪心发现一定删除最大的点周围的所有边.
然后把删除改成从小到大加入.
就没有啦
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e9;
const integer infinity=1e18;
const Integer Infinity=1e36;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=100000;
int n;
int value[MxN+5],Per[MxN+5];
int head[MxN+5],nxt[2*MxN+5],to[2*MxN+5],tot;
void AddEdge(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
struct DSU{
int father[MxN+5];
int val[MxN+5];
void Clear(){
for(int i=1;i<=n;i++){
father[i]=i;
val[i]=value[i];
}
}
int Find(int u){
if(father[u]==u){
return u;
}
return father[u]=Find(father[u]);
}
void Union(int u,int v){
int fu=Find(u),fv=Find(v);
if(fu!=fv){
father[fu]=fv;
val[fv]=max(val[fv],val[fu]);
}
}
}dsu;
bool comp(int U,int V){
return value[U]<value[V];
}
void Main(int Case){
int u,v;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&value[i]);
Per[i]=i;
}
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
AddEdge(u,v);
AddEdge(v,u);
}
dsu.Clear();
integer ans=0;
sort(Per+1,Per+n+1,comp);
for(int rnk=1;rnk<=n;rnk++){
int p=Per[rnk];
for(int i=head[p];i;i=nxt[i]){
if(value[to[i]]<value[p]||(value[to[i]]==value[p]&&to[i]<p)){
// cout<<p<<" ~ "<<to[i]<<endl;
ans=ans+value[p]+dsu.val[dsu.Find(to[i])];
dsu.Union(to[i],p);
}
}
}
printf("%lld\n",ans);
}
void Initialize(){
}
}
char InputFilename[105]="tree.in" ;
char OutputFilename[105]="tree.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
C. 欧几里得
题意
求两个最大公因数为G的数满足\(R(a,b)=H\)
int R(int A,int B){
if(B==1)return A;
if(A<B)return R(B,A);
return R(A/B,B);
}
赛时思路
我构造你个星号东西
算法概述
启动 人类智慧, 答案为:
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const integer inf=1e18;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
void Main(int Case){
integer G,R;
scanf("%lld%lld",&G,&R);
integer cur=1;
while(cur<=G){
cur*=R;
}
printf("%lld %lld\n",(cur+G-1)/G*G*R+G,(cur+G-1)/G*G);
}
void Initialize(){
}
}
char InputFilename[105]="euclid.in" ;
char OutputFilename[105]="euclid.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
scanf("%d",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
D. 没有上司的涨薪舞会
题意
有一棵树, 树上每个点代表员工.
员工在他的下属都不出厂的情况下有指定概率出厂.
员工出厂后会给他出厂的申请工资的子孙们涨工资.
求申请工资的方案是的涨工资的总期望最大.
赛时思路
从下往上处理出概率, 从上往下处理出期望, 贪心处理出答案.
没开够数组.
算法概述
可以矩阵, 但是其实直接推式子非常简单.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD=1000000007;
const int inf=1e9;
const integer infinity=1e18;
const Integer Infinity=1e36;
const ldb eps=1e-10;
const int DirectX[]={1,0,-1,0,1,1,-1,-1};
const int DirectY[]={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=100000;
int n;
int head[MxN+5],tail[MxN+5],nxt[2*MxN+5],pre[2*MxN+5],to[2*MxN+5],tot;
int father[MxN+5],mercy[MxN+5];
ldb appr[MxN+5],dp[MxN+5],receive[MxN+5];
ldb prefix[MxN+5],suffix[MxN+5];
ldb exp[MxN+5][2];
int lstnode[MxN+5],nxtnode[MxN+5];
void AddEdge(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
pre[head[u]]=tot;
head[u]=tot;
if(!tail[u]){
tail[u]=tot;
}
}
void DFS1(int p,int fa){
dp[p]=appr[p];
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa){
DFS1(to[i],p);
dp[p]=dp[p]*(1.0L-dp[to[i]]);
}
}
int lst=0;
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa){
// cout<<"::! "<<p<<" "<<to[i]<<endl;
prefix[to[i]]=prefix[lst]*(1.0L-dp[to[i]]);
lstnode[to[i]]=lst;
lst=to[i];
}
}
lst=0;
for(int i=tail[p];i;i=pre[i]){
if(to[i]!=fa){
// cout<<"!:: "<<p<<" "<<to[i]<<endl;
suffix[to[i]]=suffix[lst]*(1.0L-dp[to[i]]);
nxtnode[to[i]]=lst;
lst=to[i];
}
}
}
void DFS2(int p,int fa,ldb expect0,ldb expect1){
exp[p][0]=expect0;
exp[p][1]=expect1;
// cout<<p<<" : "<<exp[p][0]<<" "<<exp[p][1]<<endl;
// cout<<p<<" : "<<dp[p]<<" "<<prefix[p]<<" "<<suffix[p]<<endl;
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa){
DFS2(to[i],p,exp[p][0]*(1-prefix[lstnode[to[i]]]*suffix[nxtnode[to[i]]]*appr[p])+appr[p]*(exp[p][1]+mercy[p])*prefix[lstnode[to[i]]]*suffix[nxtnode[to[i]]],exp[p][0]);
}
}
}
void Main(int Case){
int u,v;
scanf("%d",&n);
for(int i=2;i<=n;i++){
scanf("%d",&father[i]);
AddEdge(father[i],i);
AddEdge(i,father[i]);
}
for(int i=1;i<=n;i++){
scanf("%Lf",&appr[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&mercy[i]);
}
suffix[0]=1.0L;
prefix[0]=1.0L;
DFS1(1,0);
exp[0][0]=0.0L;
exp[0][1]=0.0L;
DFS2(1,0,0.0L,0.0L);
ldb ans=0;
for(int p=1;p<=n;p++){
if(exp[p][1]*dp[p]>=0){
ans+=exp[p][1]*dp[p];
}
}
printf("%.6Lf\n",ans);
}
void Initialize(){
}
}
char InputFilename[105]="salary.in" ;
char OutputFilename[105]="salary.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
int TotalCase=1;
// scanf("%lld",&TotalCase);
WangManhe::Initialize();
for(int Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
20231030: NOIP2023-div2模拟赛29
A. 彩灯
题意
给一个01串,求反转一个区间后最长交错子串的最长长度.
赛时思路
简单统计一下答案,然后忘了考虑边界
算法概述
记录左右两侧能到达的最远点,枚举右端点,统计即可.
简单的代码
#include<bits/stdc++.h>
using namespace std;
const int MxN=100000;
int n,ans,Arr[MxN+5],L[MxN+5],R[MxN+5];
signed main(){
freopen("lamp.in","r",stdin);freopen("lamp.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&Arr[i]);
for(int i=1;i<=n;i++)L[i]=(i>=2&&Arr[i]!=Arr[i-1])?L[i-1]:i;
for(int i=n;i>=1;i--)R[i]=(i<n&&Arr[i]!=Arr[i+1])?R[i+1]:i;
L[0]=1;R[n+1]=n;
for(int i=1;i<=n;i++)ans=max(ans,((i<n&&Arr[i]!=Arr[i+1])?0:(R[i+1]-i))+((L[i]>=2&&Arr[L[i]]!=Arr[L[i]-1])?0:(L[i]-L[L[i]-1]))+(i-L[i]+1));
printf("%d\n",ans);
}
B. 药品试验
题意
求\(P_n\)
赛时思路
直接推式子
算法概述
考虑把式子转化成一般递推形式,然后使用换元法设\(P_1=1\)
把答案除以\(P_{2n}\)即可
简单的代码
#include<bits/stdc++.h>
#define int long long
#define MOD 1000000007
using namespace std;
const int MxN=10000000;
int Pow(int A,int B){int res=1;while(B){if(B&1)res=res*A%MOD;A=A*A%MOD;B>>=1;}return res;}
int n,A,B,a,b,c,inv,Answer[2*MxN+5];
signed main(){
freopen("experiment.in","r",stdin);freopen("experiment.out","w",stdout);
scanf("%lld%lld%lld",&n,&A,&B);
Answer[1]=1;a=(1-A+MOD)%MOD*B%MOD;b=(((1-A+MOD)%MOD)*((1-B+MOD)%MOD)%MOD+A*B%MOD-1+MOD)%MOD;c=Pow(MOD-(1-B+MOD)%MOD*A%MOD,MOD-2);
for(int i=2;i<=2*n;i++)Answer[i]=(a*Answer[i-2]%MOD+b*Answer[i-1]%MOD)%MOD*c%MOD;
printf("%lld\n",Answer[n]*Pow(Answer[2*n],MOD-2)%MOD);
}
C. 小猫钓鱼
题意
小时候都玩过的小猫钓鱼/doge
赛时思路
直接模拟,但是题读错了
算法概述
模拟,不好评价
简单的代码
#include<bits/stdc++.h>
using namespace std;
const int MxN=100;
const int MxM=1000000000;
const int MxL=100;
const int MxT=300000;
int n,m,l,s,T;
queue<int>card[MxN+5];
int Answer[MxN+5];
int All[MxT+5],tot;
map<int,int>hsh;
int alive[MxN+5],cnt;
int stk[MxT+5],top;
int instack[MxT+5];
void Play(int index,int round){
// cout<<" card: "<<card[index].front()<<endl;
if(card[index].front()==s){
if(top){
stk[++top]=card[index].front();
for(int i=1;i<=top;i++){
card[index].push(stk[i]);
instack[stk[i]]=0;
}
top=0;
card[index].pop();
if(card[index].empty()){
alive[index]=0;
Answer[index]=-round;
cnt--;
}
}
else{
stk[++top]=card[index].front();
instack[card[index].front()]=top;
card[index].pop();
if(card[index].empty()){
alive[index]=0;
Answer[index]=-round;
cnt--;
}
}
}
else if(instack[card[index].front()]){
stk[++top]=card[index].front();
for(int i=instack[card[index].front()];i<=top;i++){
card[index].push(stk[i]);
if(stk[i]!=card[index].front())instack[stk[i]]=0;
}
// cout<<"get card"<<index<<endl;
top=instack[card[index].front()]-1;
instack[card[index].front()]=0;
card[index].pop();
if(card[index].empty()){
alive[index]=0;
Answer[index]=-round;
cnt--;
}
}
else{
stk[++top]=card[index].front();
instack[card[index].front()]=top;
card[index].pop();
if(card[index].empty()){
alive[index]=0;
Answer[index]=-round;
cnt--;
}
}
}
void print(int index){
cout<<"Round"<<index<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=card[i].size();j++){
printf("%d ",All[card[i].front()]);
card[i].push(card[i].front());
card[i].pop();
}
printf("\n");
}
for(int i=1;i<=top;i++){
cout<<stk[i]<<" - ";
}
cout<<endl;
}
signed main(){
freopen("fishing.in","r",stdin);
freopen("fishing.out","w",stdout);
int u;
while(1){
scanf("%d%d%d%d%d",&n,&m,&l,&s,&T);
if(n==-1){
return 0;
}
tot=0;
for(int i=1;i<=n;i++){
while(card[i].size())card[i].pop();
for(int j=1;j<=l;j++){
scanf("%d",&u);
card[i].push(u);
All[++tot]=u;
}
Answer[i]=0;
}
sort(All+1,All+tot+1);
tot=unique(All+1,All+tot+1)-All-1;
hsh.clear();
for(int i=1;i<=tot;i++){
hsh[All[i]]=i;
instack[i]=0;
}
s=hsh[s];
cnt=n;
for(int i=1;i<=n;i++){
for(int j=1;j<=l;j++){
card[i].push(hsh[card[i].front()]);
card[i].pop();
}
alive[i]=1;
}
top=0;
for(int i=1;i<=T;i++){
for(int j=1;j<=n;j++){
if(alive[j]){
Play(j,i);
}
// print(i);
}
if(cnt<=1){
break;
}
}
for(int i=1;i<=n;i++){
if(alive[i]){
printf("%d ",(int)(card[i].size()));
}
else{
printf("%d ",Answer[i]);
}
}
printf("\n");
for(int i=1;i<=n;i++){
while(card[i].size()){
printf("%d ",All[card[i].front()]);
card[i].pop();
}
printf("\n");
}
}
}
D. 模拟旅行
题意
给一个有向图和一个点集,求点集内的两点之间最短的最短路
赛时思路
最开始想到了剪枝,认为时间复杂度可能是均值的.
然后感觉过不去,发现可以使用古老的trick,二进制分组,双log,以为能过,实际上不能.
算法概述
最短路同时在反图上最短路, 统计答案时枚举边即可.
这类题目要考虑通过某种方法统计答案,比如枚举边或点,虽然但是,古老的trick还是非常实用的.
简单的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MxN=600000;
const int MxM=2000000;
const int inf=1e18;
int n,m;
struct node{
int index;
int distance;
friend bool operator<(node U,node V){
return U.distance>V.distance;
}
};
struct Graph{
int head[MxN+5],nxt[2*MxM+5],to[2*MxM+5],tot;
int cost[2*MxM+5],vis[MxN+5];
struct Edge{
int u,v,value;
}E[2*MxM+5];
int tag[MxN+5];
int Dist[MxN+5];
priority_queue<node>Q;
void AddEdge(int u,int v,int w){
E[++tot]={u,v,w};
to[tot]=v;
cost[tot]=w;
nxt[tot]=head[u];
head[u]=tot;
}
void Dijkstra(){
for(int i=1;i<=n+1;i++){
Dist[i]=inf;
vis[i]=0;
}
Dist[n+1]=0;
while(Q.size())Q.pop();
Q.push({n+1,0});
while(!Q.empty()){
int p=Q.top().index;
Q.pop();
if(vis[p])continue;vis[p]=1;
for(int i=head[p];i;i=nxt[i]){
if( Dist[to[i]]>Dist[p]+cost[i]){
Dist[to[i]]=Dist[p]+cost[i];
if(!tag[p]){
tag[to[i]]=to[i];
}
else{
tag[to[i]]=tag[p];
}
Q.push({to[i],Dist[to[i]]});
}
}
}
}
}Ori,Rev;
signed main(){
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
int q,u,v,w;
scanf("%lld%lld%lld",&n,&m,&q);
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&w);
Ori.AddEdge(u,v,w);
Rev.AddEdge(v,u,w);
}
for(int i=1;i<=q;i++){
scanf("%lld",&u);
Ori.AddEdge(n+1,u,0);
Rev.AddEdge(n+1,u,0);
}
Ori.Dijkstra();
Rev.Dijkstra();
int ans=inf;
for(int i=1;i<=m;i++){
if(Ori.tag[Ori.E[i].u]!=Rev.tag[Ori.E[i].v]){
ans=min(ans,Ori.Dist[Ori.E[i].u]+Ori.E[i].value+Rev.Dist[Ori.E[i].v]);
}
}
printf("%lld\n",ans);
}
20231102: NOIP2023-div2模拟赛31
A. 构造题
题意
构造一个行列以及主对角线均不重复的矩形.
赛时思路
人类智慧,但是失败
面对大样例的编程方法
依葫芦画瓢的胜利!
算法概述
这题就不该给大样例
简单的代码
#include<bits/stdc++.h>
using namespace std;
const int MxN=1000;
int n;
int Answer[MxN+5][MxN+5];
signed main(){
freopen("squ.in","r",stdin);
freopen("squ.out","w",stdout);
scanf("%d",&n);
if(n&1){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",(i+j-2)%n+1);
}
printf("\n");
}
}
else{
for(int i=1;i<=n;i++)Answer[1][i]=i;
swap(Answer[1][n],Answer[1][n-1]);
for(int i=1;i<=n;i++)Answer[i][1]=i;
swap(Answer[2][1],Answer[n][1]);
for(int i=2;i<n;i+=2)Answer[i/2+1][n]=i;
for(int i=1;i<n-1;i+=2)Answer[n/2+i/2+1][n]=i;
for(int i=2;i<n;i+=2)Answer[n][i/2]=i;
for(int i=1;i<n;i+=2)Answer[n][n/2+i/2]=i;
Answer[n][n]=n;
for(int i=2;i<n;i++){
for(int j=2;j<n;j++){
if(i==j+1){
Answer[i][j]=n;
}
else if(i+j<=n){
Answer[i][j]=(i+j-2+n)%n+1;
}
else{
Answer[i][j]=(i+j-1+n)%n+1;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",Answer[i][j]);
}
printf("\n");
}
}
}
B. UTF-8
题意
给一个01序列,求它可能表示的数列个数.
赛时思路
不是大模拟,胜似大模拟
算法概述
dp套数位dp,究极答辩.
简单的代码
#include<bits/stdc++.h>
using namespace std;
const int MxN=100000;
const long long MOD=1000000007;
const int limit1=201551;
const int limit2=65535;
const int limit3=2047;
const int limit4=127;
int n;
long long dp[MxN+5];
char Str[MxN+5][10];
int Flag[MxN+5][10];
int tot;
char num[30];
int ndp[30],vis[30],limit;
int DP(int d,int top){
if(d>tot){
// cout<<123123<<endl;
return 1;
}
// cout<<d<<endl;
if(!top){
if(vis[d]){
return ndp[d];
}
vis[d]=1;
ndp[d]=0;
if(num[d]!='0'){
ndp[d]=(ndp[d]+DP(d+1,0))%MOD;
}
if(num[d]!='1'){
ndp[d]=(ndp[d]+DP(d+1,0))%MOD;
}
return ndp[d];
}
else{
int res=0;
if(limit&(1<<(tot-d))){
if(num[d]!='1'){
res=(res+DP(d+1,0))%MOD;
}
if(num[d]!='0'){
res=(res+DP(d+1,1))%MOD;
}
}
else{
if(num[d]!='1'){
res=(res+DP(d+1,1))%MOD;
}
// else{
// cout<<456456<<endl;
// }
}
return res;
}
}
int DDPP(int u){
limit=u;
memset(vis,0,sizeof(vis));
memset(ndp,0,sizeof(ndp));
int ret=DP(1,1);
// cout<<"::"<<ret<<endl;
return ret;
}
signed main(){
freopen("utf.in","r",stdin);
freopen("utf.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",Str[i]+1);
for(int j=0;j<=4;j++){
Flag[i][j]=1;
for(int k=1;k<=j;k++){
if(Str[i][k]=='0'){
Flag[i][j]=0;
break;
}
}
Flag[i][j]&=(Str[i][j+1]!='1');
// cout<<Flag[i][j]<<" ";
}
// cout<<endl;
}
// cout<<Flag[1][4]<<endl;
dp[0]=1;
for(int i=1;i<=n;i++){
dp[i]=0;
if(i>=1&&Flag[i][0]){
tot=0;
for(int j=2;j<=8;j++)num[++tot]=Str[i][j];
int res=(DDPP(limit4)-DDPP(0))%MOD;
dp[i]=(dp[i]+res*dp[i-1]%MOD)%MOD;
// cout<<i<<" "<<dp[i]<<endl;
}
// cout<<i<<endl;
if(i>=2&&Flag[i][1]&&Flag[i-1][2]){
tot=0;
for(int j=4;j<=8;j++)num[++tot]=Str[i-1][j];
for(int j=3;j<=8;j++)num[++tot]=Str[i][j];
int res=(DDPP(limit3)-DDPP(limit4)+MOD)%MOD;
dp[i]=(dp[i]+res*dp[i-2]%MOD)%MOD;
// cout<<i<<" "<<dp[i]<<endl;
}
// cout<<i<<endl;
if(i>=3&&Flag[i][1]&&Flag[i-1][1]&&Flag[i-2][3]){
tot=0;
for(int j=5;j<=8;j++)num[++tot]=Str[i-2][j];
for(int j=3;j<=8;j++)num[++tot]=Str[i-1][j];
for(int j=3;j<=8;j++)num[++tot]=Str[i][j];
int res=(DDPP(limit2)-DDPP(limit3)+MOD)%MOD;
dp[i]=(dp[i]+res*dp[i-3]%MOD)%MOD;
// cout<<i<<" "<<dp[i]<<endl;
}
// cout<<i<<Flag[i][1]<<Flag[i-1][1]<<Flag[i-2][1]<<Flag[i-3][4]<<endl;
if(i>=4&&Flag[i][1]&&Flag[i-1][1]&&Flag[i-2][1]&&Flag[i-3][4]){
tot=0;
for(int j=6;j<=8;j++)num[++tot]=Str[i-3][j];
for(int j=3;j<=8;j++)num[++tot]=Str[i-2][j];
for(int j=3;j<=8;j++)num[++tot]=Str[i-1][j];
for(int j=3;j<=8;j++)num[++tot]=Str[i][j];
int res=(DDPP(limit1)-DDPP(limit2)+MOD)%MOD;
dp[i]=(dp[i]+res*dp[i-4]%MOD)%MOD;
// for(int i=1;i<=21;i++)cout<<num[i];cout<<" ";
// cout<<i<<" "<<dp[i]<<" "<<res<<endl;
}
}
printf("%lld\n",dp[n]);
}
C. 括号串
题意
给一个括号串,初始为"()"
支持三种操作:
- 在串的后面加一个"()"
- 在左边加一个"(",右边加一个")"
- 消除之前某操作的影响.
赛时思路
使用矩阵通过.
算法概述
矩阵中记录答案,后缀可行区间,再辅助一个1帮助转移.
转移的时候分别构造两种矩阵,在线段树上单点修改即可.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int integer
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=200000;
int n;
struct Matrix{
int A[3][3];
int& operator()(int u,int v){
return A[u-1][v-1];
}
friend Matrix operator*(Matrix& U,Matrix& V){
Matrix ret;
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
ret(i,j)=0;
for(int k=1;k<=3;k++){
ret(i,j)+=U(i,k)*V(k,j);
}
}
}
return ret;
}
void Print(){
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
printf("%lld ",thi(i,j));
}
printf("\n");
}
}
}Ori,OP1,OP2;
namespace Segment_Tree{
Matrix rng[4*MxN+5];
void PushUp(int p){
rng[p]=rng[p<<1]*rng[p<<1|1];
}
void Build(int L,int R,int p){
if(L==R){
rng[p]=Ori;
return;
}
int mid=(L+R)>>1;
Build(L,mid,p<<1);
Build(mid+1,R,p<<1|1);
PushUp(p);
}
void Modify(int L,int R,int l,Matrix mat,int p){
if(L==R){
rng[p]=mat;
return;
}
int mid=(L+R)>>1;
if(l<=mid){
Modify(L,mid,l,mat,p<<1);
}
else{
Modify(mid+1,R,l,mat,p<<1|1);
}
PushUp(p);
}
}
int op[MxN+5];
int depth[MxN+5],link[MxN+5];
void Main(int Case){
scanf("%lld",&n);
Segment_Tree::Build(1,n,1);
for(int i=1;i<=n;i++){
scanf("%lld",&op[i]);
if(op[i]==1){
Segment_Tree::Modify(1,n,i,OP1,1);
depth[i]=0;
}
else if(op[i]==2){
Segment_Tree::Modify(1,n,i,OP2,1);
depth[i]=0;
}
else{
scanf("%lld",&link[i]);
depth[i]=depth[link[i]]+1;
if(op[link[i]]==3){
link[i]=link[link[i]];
}
if(depth[i]&1){
Segment_Tree::Modify(1,n,link[i],Ori,1);
}
else{
if(op[link[i]]==1){
Segment_Tree::Modify(1,n,link[i],OP1,1);
}
else{
Segment_Tree::Modify(1,n,link[i],OP2,1);
}
}
}
Matrix ans;
ans(1,1)=1;ans(1,2)=0;ans(1,3)=0;//答案
ans(2,1)=1;ans(2,2)=0;ans(2,3)=0;//最外层连续个数
ans(3,1)=1;ans(3,2)=0;ans(3,3)=0;//1
ans=Segment_Tree::rng[1]*ans;
// ans.Print();
printf("%lld\n",ans(1,1));
}
}
void Initialize(){
Ori(1,1)=1;Ori(1,2)=0;Ori(1,3)=0;
Ori(2,1)=0;Ori(2,2)=1;Ori(2,3)=0;
Ori(3,1)=0;Ori(3,2)=0;Ori(3,3)=1;
OP1(1,1)=1;OP1(1,2)=1;OP1(1,3)=1;
OP1(2,1)=0;OP1(2,2)=1;OP1(2,3)=1;
OP1(3,1)=0;OP1(3,2)=0;OP1(3,3)=1;
OP2(1,1)=1;OP2(1,2)=0;OP2(1,3)=1;
OP2(2,1)=0;OP2(2,2)=0;OP2(2,3)=1;
OP2(3,1)=0;OP2(3,2)=0;OP2(3,3)=1;
}
}
char InputFilename[105]="dydy.in" ;
char OutputFilename[105]="dydy.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
D. 博弈
题意
给一个每列格子数量不同的棋盘,依次随机置入格子,求第一次有一行被填满时,这行的值的期望.
赛时思路
骗到5分,走人.
算法概述
第一步, 注意到取模对题目的化简, 我们可以先处理出这个数的值的模,以及这个数占用的位数的模(10的次方)
然后, 计算一个卡片后面期望有多少位.
然后, 令\(dp[i][j]\)表示前i行放j个的概率.
最后统计答案时,要把当前物品扣掉,这里学到了一点生成函数的知识.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int integer
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000009;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
int Pow(int u,int v){
int res;
while(v){
if(v&1){
res=res*u%MOD;
}
u=u*u%MOD;
v>>=1;
}
return res;
}
namespace WangManhe{
const int MxN=300;
int fac[2*MxN+5],fac_inv[2*MxN+5];
int C(int u,int v){
return fac[u]*fac_inv[v]%MOD*fac_inv[u-v]%MOD;
}
int n,m;
int len[MxN+5];
int value[MxN+5],level[MxN+5],length[MxN+5];
int dp[MxN+5][MxN+5],E[MxN+5][MxN+5];
char Str[MxN+5][MxN+5];
void Main(int Case){
scanf("%lld%lld",&n,&m);
int sum=0;
for(int i=1;i<=m;i++){
scanf("%lld",&length[i]);
sum+=length[i];
}
for(int i=1;i<=n;i++){
scanf("%s",Str[i]+1);
level[i]=1;
len[i]=strlen(Str[i]+1);
for(int j=1;j<=len[i];j++){
value[i]=(10*value[i]%MOD+Str[i][j]-'0')%MOD;
level[i]=10*level[i]%MOD;
}
}
E[0][0]=1;
for(int i=1;i<=n;i++){
E[i][0]=E[i-1][0];
for(int j=1;j<=i;j++){
E[i][j]=(E[i-1][j-1]*level[i]%MOD+E[i-1][j])%MOD;
}
}
int ans=0;
for(int i=1;i<=m;i++){
Clean(dp);
dp[0][0]=1;
for(int j=1;j<=m;j++){
for(int k=0;k<=n;k++){
if(dp[j-1][k]){
if(i==j){
dp[j][k+length[j]-1]=(dp[j][k+length[j]-1]+dp[j-1][k]%MOD)%MOD;
}
else{
for(int l=0;l<=length[j]-1;l++){
dp[j][k+l]=(dp[j][k+l]+dp[j-1][k]*C(length[j],l)%MOD)%MOD;
}
}
}
}
}
for(int j=1;j<=n;j++){
if(dp[m][j-1]){
int res=0;
for(int k=1;k<j;k++){
for(int l=0;l<=length[i];l++){
E[j-1][l+1]=(E[j-1][l+1]-E[j-1][l]*level[k]%MOD+MOD)%MOD;
}
for(int l=1;l<=length[i];l++){
res=(res+E[j-1][l-1]*value[k]%MOD*fac[l-1]%MOD*(length[i]-l)%MOD*fac[j-l-1])%MOD;
}
for(int l=2;l<=length[i];l++){
res=(res+E[j-1][l-2]*level[j]%MOD*value[k]%MOD*fac[l-1]%MOD*fac[j-l])%MOD;
}
for(int l=length[i];l>=0;l--){
E[j-1][l+1]=(E[j-1][l+1]+E[j-1][l]*level[k]%MOD)%MOD;
}
}
for(int l=1;l<=length[i];l++){
res=(res+E[j-1][l-1]*value[j]%MOD*fac[l-1]%MOD*fac[j-l])%MOD;
}
ans=(ans+res*dp[m][j-1]%MOD*fac_inv[sum]%MOD*fac[sum-j])%MOD;
}
}
}
printf("%lld\n",ans);
}
void Initialize(){
fac[0]=fac_inv[0]=1;
for(int i=1;i<=2*MxN;i++){
fac[i]=fac[i-1]*i%MOD;
}
fac_inv[2*MxN]=Pow(fac[2*MxN],MOD-2);
for(int i=2*MxN-1;i>=0;i--){
fac_inv[i]=fac_inv[i+1]*(i+1)%MOD;
}
}
}
char InputFilename[105]="game.in" ;
char OutputFilename[105]="game.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
20231103: NOIP2023-div2模拟赛32
A. 点分治
题意
按题中方法生成树,求距离一个点距离为k的点数
赛时思路
看一下样例解释发现是组合数.
直接计算即可, 但是不取模.
算法概述
注意到树的每层分别是对应的组合数, 对于每一个1数码减去对之前的覆盖即可, 注意最后要压一个n进去
简单的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MxN=10000000;
const int MxK=1000000;
const int MOD=998244353;
int Pow(int u,int v){
int res=1;
while(v){
if(v&1){
res=res*u%MOD;
}
u=u*u%MOD;
v>>=1;
}
return res;
}
int fac[MxN+5],fac_inv[MxN+5];
int n,q;
int m,digits[MxK+5],dist;
int C(int u,int v){
if(u<0||v<0||u<v){
return 0;
}
return fac[u]*fac_inv[v]%MOD*fac_inv[u-v]%MOD;
}
void Solve(){
scanf("%lld",&m);
for(int i=1;i<=m;i++){
scanf("%lld",&digits[i]);
}
scanf("%lld",&dist);
sort(digits+1,digits+m+1);
int ans=0;
digits[++m]=n;
for(int i=1;i<=m;i++,dist--){
ans=(ans+C(digits[i],dist)-(i!=1)*C(digits[i-1],dist-1)+MOD)%MOD;
}
printf("%lld\n",ans);
}
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%lld%lld",&n,&q);
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*i%MOD;
}
fac_inv[n]=Pow(fac[n],MOD-2);
for(int i=n-1;i>=0;i--){
fac_inv[i]=fac_inv[i+1]*(i+1)%MOD;
}
for(int i=1;i<=q;i++){
Solve();
}
}
B. 卷王 tzc 和 tree
题意
给一颗树, 每次往一个点上布置任务, 任务如果可以等量分配给孩子就会分下去,求有多少任务不会被分下去.
赛时思路
分解因子! 启发式合并! 根号乘log! 寄!
算法概述
记忆化记录, 缩掉度为1的链即可. 理论复杂度四次根号下.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=100000;
int n,q;
int head[MxN+5],to[2*MxN+5],nxt[2*MxN+5],tot;
int father[MxN+5],childs[MxN+5];
int tag[MxN+5];
map<int,int>dp[MxN+5];
void AddEdge(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
void Link(int p,int fa){
for(int i=head[p];i;i=nxt[i]){
Link(to[i],p);
while(childs[to[i]]==1){
to[i]=to[head[to[i]]];
}
}
}
int Solve(int u,int v){
if(!childs[u])return 0;
if(v%childs[u])return v;
if(!dp[u].count(v)){
int res=0;
for(int i=head[u];i;i=nxt[i]){
res+=Solve(to[i],v/childs[u]);
}
dp[u][v]=res;
}
return dp[u][v];
}
void Main(int Case){
int u,v;
scanf("%d",&n);
for(int i=2;i<=n;i++){
scanf("%d",&father[i]);
AddEdge(father[i],i);
childs[father[i]]++;
}
Link(1,0);
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d%d",&u,&v);
printf("%d\n",Solve(u,v));
}
}
void Initialize(){
}
}
char InputFilename[105]="tree.in" ;
char OutputFilename[105]="tree.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
C. 塔
题意
一个三角形的塔上有若干守卫, 每次可以花费3消灭一个守卫. 或者花费巨资消除一个点下面的所有守卫
赛时思路
打暴力, 挂掉, 假了.
算法概述
发现只有下根号n层会出巨资, 在这里进行滚动数组DP, 对于上面的, 直接使用1操作即可.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=100000;
const int T=1000;
int n,m,ans;
int dp[MxN+5];
int G[MxN+5][T+5];
void Main(int Case){
int u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
if(u>=n-T+1)G[v][n-u+1]=1;
else ans+=3;
}
for(int i=n;i>=1;i--){
int prefix=0;
for(int j=min(T,n);j;j--){
prefix+=G[i][j];
dp[j]=dp[j-1]+3*prefix;
}
dp[0]+=3*prefix;
for(int j=1;j<=min(T,n);j++){
dp[0]=min(dp[0],(j+1)*j/2+2+dp[j+1]);
}
for(int j=1;j<=min(T,n);j++){
dp[j]=min(dp[j],dp[0]);
}
}
printf("%d\n",dp[0]+ans);
}
void Initialize(){
}
}
char InputFilename[105]="tower.in" ;
char OutputFilename[105]="tower.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
D. 军队
题意
给一个矩阵和若干矩形, 覆盖次数大于等于k的值是雌性的.
查询在若干行每行选若干个的最大雌性个数和雄性个数的积.
赛时思路
寄.
算法概述
使用扫描线求出每行的雌性个数, 使用均值不等式来计算出最大答案, 注意判断是否能取到均值.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=300000;
const int MxK=10;
const int MxQ=1000000;
int n,m,c,limit,q;
int bound[MxN+5],cnt[MxN+5];
integer prefix[MxN+5],sqrprefix[MxN+5];
struct ScanLine{
int y1,y2;
int value;
};
vector<ScanLine>SCL[MxN+5];
struct Segment_Tree{
int root,tot;
int child[2*MxN+5][2];
int count[2*MxN+5][MxK+5];
int tag[2*MxN+5];
void PushUp(int L,int R,int p){
if(L==R){
for(int i=0;i<=limit;i++){
count[p][i]=(tag[p]>=i);
}
return;
}
for(int i=0;i<=limit;i++){
if(tag[p]>=i){
count[p][i]=R-L+1;
}
else{
count[p][i]=count[child[p][0]][i-tag[p]]+count[child[p][1]][i-tag[p]];
}
}
}
void Build(int L,int R,int &p){
if(!p)p=++tot;
if(L==R){
PushUp(L,R,p);
return;
}
int mid=(L+R)>>1;
Build(L,mid,child[p][0]);
Build(mid+1,R,child[p][1]);
PushUp(L,R,p);
}
void Modify(int L,int R,int l,int r,int c,int &p){
if(!p)p=++tot;
if(L==l&&R==r){
tag[p]+=c;
PushUp(L,R,p);
return;
}
int mid=(L+R)>>1;
if(r<=mid){
Modify(L,mid,l,r,c,child[p][0]);
}
else if(l>mid){
Modify(mid+1,R,l,r,c,child[p][1]);
}
else{
Modify(L,mid,l,mid,c,child[p][0]);
Modify(mid+1,R,mid+1,r,c,child[p][1]);
}
PushUp(L,R,p);
}
int Query(){
return count[root][limit];
}
}SGT;
void Main(int Case){
int x1,y1,x2,y2,u,v;
scanf("%d%d%d%d%d",&n,&m,&c,&limit,&q);
for(int i=1;i<=c;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
SCL[x1].push_back({y1,y2,1});
SCL[x2+1].push_back({y1,y2,-1});
}
SGT.Build(1,m,SGT.root);
for(int scan=1;scan<=n;scan++){
for(auto Line:SCL[scan]){
SGT.Modify(1,m,Line.y1,Line.y2,Line.value,SGT.root);
}
int res=SGT.Query();
// cout<<res<<endl;
bound[scan]=min(res,m-res);
}
sort(bound+1,bound+n+1,greater<int>());
for(int i=1;i<=n;i++){
cnt[bound[i]]++;
prefix[i]=prefix[i-1]+bound[i];
sqrprefix[i]=sqrprefix[i-1]+bound[i]*bound[i];
}
for(int i=m;i>=0;i--){
cnt[i]+=cnt[i+1];
}
for(int i=1;i<=q;i++){
scanf("%d%d",&u,&v);
integer half=v/2;
if(bound[u]>=half){
printf("%lld\n",half*(v-half)*u);
}
else{
printf("%lld\n",half*(v-half)*cnt[half]+v*(prefix[u]-prefix[cnt[half]])-(sqrprefix[u]-sqrprefix[cnt[half]]));
}
}
}
void Initialize(){
}
}
char InputFilename[105]="army.in" ;
char OutputFilename[105]="army.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
20231106: NOIP2023-div2模拟赛34
A. 取整
题意
给一个分数,这个分数的整数倍向下取整构成一个数列.
求不包含在这个数列中的第k个数.
赛时思路
简单二分题.
算法概述
首先考虑二分答案,然后直接计算前面有多少个数在数列中,减一下比较即可.
简单的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int p,q,k;
int count(int x){
return (int)((__int128_t)((__int128_t)(x+1)*q-1)/p);
}
void solve(){
scanf("%lld%lld%lld",&p,&q,&k);
// for(int i=0;i<=10;i++)cout<<" :"<<count(i)<<endl;
int l=0,r=1e18,mid,res=-1;
while(l<=r){
int mid=(l+r)>>1;
if(mid-count(mid)+1>=k){
r=mid-1;
res=mid;
}
else{
l=mid+1;
}
}
if(res==-1||res+1-count(res)!=k){
res=-1;
}
printf("%lld\n",res);
}
signed main(){
freopen("rounding.in","r",stdin);
freopen("rounding.out","w",stdout);
int T;
scanf("%lld",&T);
while(T--){
solve();
}
}
B. 缩略
题意
一个01字符串,连续两个相同的字符可以变成另一个字符,求最后得到的不同字符串数.
赛时思路
D2T2,D1T1,AGCE,NOIPT1,luogu黑
写个锤子.
考虑前缀和优化dp,寄.
发现算重问题,大寄特寄.
算法概述
** 模 3 同 余 **
这谁能想到啊.
但是有这个结论就好了, 前缀和只要是相等的都可以转移过来, 直接dp即可, 注意特判答案为1 的情况.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int integer
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =998244353;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=50000;
int n;
char Str[MxN+5];
int prefix[MxN+5];
int pre[3];
int dp[MxN+5];
void Main(int Case){
scanf("%s",Str+1);
n=strlen(Str+1);
int flag=0;
for(int i=1;i<=n;i++){
flag|=(i>=2&&Str[i]==Str[i-1]);
prefix[i]=(prefix[i-1]+Str[i]-'x'+1)%3;
dp[i]=((prefix[i]>=1)+dp[pre[0]]+dp[pre[1]]+dp[pre[2]]-dp[pre[prefix[i]]])%MOD;
pre[prefix[i]]=i;
}
if(flag){
printf("%lld\n",dp[n]);
}
else{
printf("%lld\n",1);
}
}
void Initialize(){
}
}
char InputFilename[105]="abbreviate.in" ;
char OutputFilename[105]="abbreviate.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
C. 最小生成树
题意
一个简单无向图,对于每个i,求1号节点度数为i的最小生成数.
满足1和每个节点都有连边,且1不是割点.
赛时思路
把1删掉,建立最小生成树,随便贪心一下(按距离贪心,然后边排序搞).
中间有一部寄了,但是最后神使鬼差地对了.
但是并查集没有路径压缩啊啊啊啊啊啊啊啊啊啊啊啊啊啊.
算法概述
实际上,应该是按代价排序,然后选贡献最小的.
实际代码中,放在kruskal中处理即可.
简单的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MxN=300000,MxM=600000;
int n,m,tot,mndist;
int dist[MxN+5];
int value[MxN+5];
struct Edge{
int u,v;
int value;
friend bool operator<(Edge u,Edge v){
return u.value<v.value;
}
}E[MxM+5];
struct DSU{
int father[MxN+5];
int Find(int u){
if(father[u]==u){
return u;
}
return father[u]=Find(father[u]);
}
void Union(int u,int v,int val){
int fu=Find(u),fv=Find(v);
if(fu==fv)return;
if(dist[fu]<dist[fv])swap(fu,fv);
value[fu]=dist[fu]-val;
// cout<<fu<<" "<<value[fu]<<endl;
father[fu]=fv;
}
void clear(){
mndist=1e18;
for(int i=1;i<=n;i++){
if(i>1)
mndist=min(mndist,dist[i]);
father[i]=i;
value[i]=-1e18;
// cout<<dist[i]<<" ";
}
// cout<<endl;
}
}dsu;
signed main(){
freopen("rootedmst.in","r",stdin);
freopen("rootedmst.out","w",stdout);
int u,v,w;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&w);
if(u==1)dist[v]=w;
else if(v==1)dist[u]=w;
else E[++tot]={u,v,w};
}
sort(E+1,E+tot+1);
int ans=0;
dsu.clear();
for(int i=1;i<=tot;i++){
if(dsu.Find(E[i].u)!=dsu.Find(E[i].v)){
ans+=E[i].value;
dsu.Union(E[i].u,E[i].v,E[i].value);
}
}
// cout<<ans<<endl;
sort(value+2,value+n+1);
for(int i=2;i<=n;i++){
if(value[i]==-1e18){
value[i]=mndist;
}
ans+=value[i];
printf("%lld ",ans);
}
}
D. 树套树
题意
在一个树上,每个节点维护一个BST.
支持链上每个节点的BST插入一个点.
支持查询在一个节点上查询某个值时经过的权值和.
赛时思路
暴力,走人.
算法概述
把询问离线到树上,考虑树上差分的思想,配合线段树合并.
在线段树上,可以在pushup的时候查询,以辅助计算答案,最后时间复杂度双log.
实际实现比较炸裂.
简单的代码
/*
* Contest:
* Problem:
* Date:
* 树套数套竖套束套术
*/
#include<bits/stdc++.h>
using namespace std;
#define int int
#define uint unsigned int
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=200000;
int n,m,q;
int head[MxN+5],nxt[2*MxN+5],to[2*MxN+5],tot;
int depth[MxN+5],top[MxN+5],father[MxN+5],son[MxN+5],siz[MxN+5];
struct QueryStructure{
int type;
int u,v;
uint value;
int tick;
}Q[MxN+5];
int Answer[MxN+5];
struct Operation{
int index;
int value;
int tick;
};
vector<Operation>Mod[MxN+5];
vector<Operation>Que[MxN+5];
uint All[MxN+5];
map<uint,int>hsh;
void AddEdge(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
void DFS1(int p,int fa){
siz[p]=1;
father[p]=fa;
depth[p]=depth[fa]+1;
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa){
DFS1(to[i],p);
siz[p]+=siz[to[i]];
if(siz[to[i]]>siz[son[p]]){
son[p]=to[i];
}
}
}
}
void DFS2(int p,int fa,int tp){
top[p]=tp;
if(son[p]){
DFS2(son[p],p,tp);
}
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa&&to[i]!=son[p]){
DFS2(to[i],p,to[i]);
}
}
}
int LCA(int u,int v){
while(top[u]!=top[v]){
if(depth[top[u]]<depth[top[v]]){
swap(u,v);
}
u=father[top[u]];
}
if(depth[u]>depth[v]){
swap(u,v);
}
return u;
}
struct Segment_Tree{
int tot,minvalue;
uint res;
int root[MxN+5];
int child[30*MxN+5][2];
int value[30*MxN+5];
int siz[30*MxN+5];
uint sum[30*MxN+5];
uint A[MxN+5];
int tim[MxN+5];
int Calc(int L,int R,int val,int p){
if(!p)return 0;
if(L==R){
return (value[p]<val)*A[L];
}
int mid=(L+R)>>1;
if(!child[p][0]||value[child[p][0]]>val){
return Calc(mid+1,R,val,child[p][1]);
}
else{
return Calc(L,mid,val,child[p][0])+sum[child[p][1]];
}
}
void PushUp(int L,int R,int p){
int mid=(L+R)>>1;
value[p]=min(value[child[p][0]],value[child[p][1]]);
if(child[p][1]){
sum[child[p][1]]=Calc(mid+1,R,value[child[p][0]],child[p][1]);
}
}
void Modify(int L,int R,int l,int val,int& p){
if(!p)p=++tot;
if(L==R){
siz[p]+=val;
if(siz[p]){
value[p]=tim[L];
}
else{
value[p]=inf;
}
return;
}
int mid=(L+R)>>1;
if(l<=mid){
Modify(L,mid,l,val,child[p][0]);
}
else{
Modify(mid+1,R,l,val,child[p][1]);
}
PushUp(L,R,p);
}
void Query(int L,int R,int l,int r,int p){
if(!p)return;
if(L==l&&R==r){
res+=Calc(L,R,minvalue,p);
minvalue=min(minvalue,value[p]);
return;
}
int mid=(L+R)>>1;
if(r<=mid){
Query(L,mid,l,r,child[p][0]);
}
else if(l>mid){
Query(mid+1,R,l,r,child[p][1]);
}
else{
Query(L,mid,l,mid,child[p][0]);
Query(mid+1,R,mid+1,r,child[p][1]);
}
}
void Merge(int L,int R,int &u,int &v){
if(!u||!v){
u=u|v;
return;
}
if(L==R){
siz[u]+=siz[v];
if(siz[u]){
value[u]=tim[L];
}
else{
value[u]=inf;
}
return;
}
int mid=(L+R)>>1;
Merge(L,mid,child[u][0],child[v][0]);
Merge(mid+1,R,child[u][1],child[v][1]);
PushUp(L,R,u);
}
uint Query(int p,int l,int r,int val){
minvalue=val;res=0;
Query(1,m,l,r,root[p]);
return res;
}
}SGT1,SGT2;
void DFS3(int p,int fa){
for(int i=head[p];i;i=nxt[i]){
if(to[i]!=fa){
DFS3(to[i],p);
SGT1.Merge(1,m,SGT1.root[p],SGT1.root[to[i]]);
SGT2.Merge(1,m,SGT2.root[p],SGT2.root[to[i]]);
}
}
for(auto q:Mod[p]){
// cout<<q.index<<" "<<p<<endl;
SGT1.Modify(1,m,q.index,q.value,SGT1.root[p]);
SGT2.Modify(1,m,m-q.index+1,q.value,SGT2.root[p]);
}
for(auto q:Que[p]){
Answer[q.tick]=SGT1.Query(p,q.index,m,q.tick)+SGT2.Query(p,m-q.index+1,m,q.tick);
// cout<<q.tick<<" asdasd "<<Answer[q.tick]<<endl;
}
}
void Main(int Case){
int op,u,v;uint w;
scanf("%d%d",&n,&q);
for(int i=2;i<=n;i++){
scanf("%d%d",&u,&v);
AddEdge(u,v);
AddEdge(v,u);
}
DFS1(1,0);
DFS2(1,0,1);
for(int i=1;i<=q;i++){
scanf("%d",&Q[i].type);
if(Q[i].type==1){
scanf("%d%d%u",&Q[i].u,&Q[i].v,&Q[i].value);
}
else{
scanf("%d%u",&Q[i].u,&Q[i].value);
}
Q[i].tick=i;
All[++m]=Q[i].value;
}
sort(All+1,All+m+1);
m=unique(All+1,All+m+1)-All-1;
for(int i=1;i<=m;i++){
hsh[All[i]]=i;
}
for(int i=1;i<=q;i++){
Q[i].value=hsh[Q[i].value];
SGT1.tim[Q[i].value]=SGT2.tim[m-Q[i].value+1]=i;
SGT1.A[Q[i].value]=SGT2.A[m-Q[i].value+1]=All[Q[i].value];
if(Q[i].type==1){
int lcauv=LCA(Q[i].u,Q[i].v);
Mod[Q[i].u].push_back((Operation){(int)(Q[i].value),1,i});
Mod[Q[i].v].push_back((Operation){(int)(Q[i].value),1,i});
Mod[lcauv].push_back((Operation){(int)(Q[i].value),-1,i});
Mod[father[lcauv]].push_back((Operation){(int)(Q[i].value),-1,i});
}
else{
Que[Q[i].u].push_back((Operation){(int)(Q[i].value),1,i});
}
}
DFS3(1,0);
for(int i=1;i<=q;i++){
if(Q[i].type==2){
printf("%u\n",Answer[i]);
}
}
}
void Initialize(){
SGT1.value[0]=inf;
SGT2.value[0]=inf;
}
}
char InputFilename[105]="tree.in" ;
char OutputFilename[105]="tree.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
20231107: NOIP2023-div2模拟赛35
A. 区间
题意
给若干个区间,需要选择尽可能多对,是的每对不相交.
赛时思路
想到了反悔贪心,但是,之前没写过反悔贪心,险些不会写,试了多种stl写法才找到合适的方法.
算法概述
按左端点排序,扫描所有的,如果能和之前的匹配就匹配,否则如果能成为之前的某一个的更优替代就替代,再不行就扔垃圾桶.
简单的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=500000;
int n,ans;
struct Range{
int l,r;
}rng[maxn+5];
multiset<int>freerbound;
multiset<int>usedrbound;
bool vis[maxn+5];
bool cmp1(Range u,Range v){
if(u.l==v.l){
return u.r>v.r;
}
return u.l<v.l;
}
signed main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&rng[i].l,&rng[i].r);
}
sort(rng+1,rng+n+1,cmp1);
for(int i=1;i<=n;i++){
if(!freerbound.empty()&&(*(freerbound.begin()))<rng[i].l){
//used a free rbound to add the answer
ans++;
usedrbound.insert(rng[i].r);
// usedrbound.insert(*(freerbound.begin()));
freerbound.erase(freerbound.begin());
}
else if(!usedrbound.empty()&&(*(usedrbound.begin()))<rng[i].r){
//replace a used rbound
freerbound.insert(*(usedrbound.begin()));
usedrbound.erase(usedrbound.begin());
usedrbound.insert(rng[i].r);
}
else{
freerbound.insert(rng[i].r);
}
}
printf("%lld\n",ans);
}
B. 排序
题意
一个序列,每次对一个区间操作,构造一个操作序列是的这个操作序列能使这个序列有序.
对一个子区间操作的结果是把距离左端点为奇数的点按原来顺序放前面,偶数的按原来顺序放后面
赛时思路
想了半天没构造出来,打个暴力还挂了.
算法概述
纯逆天.
类似冒泡排序或选择排序(看具体写法),反正每次确定一个点,消耗的次数看起来单log,实际上均摊O(n),问题是这玩意是O(n)而不是n,所以带常数,所以会寄.
解决方法是先若干次随机操作打乱序列,然后发现还是不行.
接着加while跑好几遍,跑到不大于6000就停止.
这时有概率通过,使用特殊的随机数种子也可以通过,但问题在于,这都是赛后卡常技巧(比如巧换种子,筛种子等),离线赛出个这我个人认为不是很合理.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937 mt_rand(2212);
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const Integer Infinity =1e36;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=3000;
int n;
int Ori[MxN+5];
int Arr[MxN+5];
int tmp[MxN+5];
int pos[MxN+5];
vector<pair<int,int>>ans;
void Modify(int L,int R){
ans.push_back({L,R});
int j=L;
for(int i=L+1;i<=R;i+=2){
tmp[i]=pos[j++];
}
for(int i=L;i<=R;i+=2){
tmp[i]=pos[j++];
}
for(int i=L;i<=R;i++){
pos[i]=tmp[i];
Arr[pos[i]]=i;
}
}
void Main(int Case){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&Ori[i]);
}
do{
for(int i=1;i<=n;i++){
Arr[i]=Ori[i];
pos[Arr[i]]=i;
}
for(int i=1;i<=10;i++){
int l=rand()%n+1,r=rand()%n+1;
if(l>r)swap(l,r);
Modify(l,r);
}
for(int i=n;i;i--){
int j=Arr[i];
for(j=Arr[i];2*j<=i;j*=2){
Modify(1,2*j);
}
if(i>j){
Modify(2*j-i+1,i);
}
}
}while(ans.size()>6000);
printf("%d\n",ans.size());
for(int i=ans.size()-1;i>=0;i--){
printf("%d %d\n",ans[i].first,ans[i].second);
}
return;
}
void Initialize(){
}
}
char InputFilename[105]="sort.in" ;
char OutputFilename[105]="sort.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
C. 分组
题意
原题: IOI 2015 teams
每个人一个可以接受的组员个数区间, 每次询问给若干个小组的组员个数, 问是否存在合法分组方案.
赛时思路
这tm是NOIP模拟赛T3??
赛后:NOIPlus模拟赛??????
算法概述
把每个人想成一个点,横纵坐标就是区间的上下界, 一个组可以放的人就是这个组的人数对应在坐标轴的点的左上方的人.
把这些组按照大小排序, 发现这些矩形满足单调栈的性质, 每次使用主席树维护纵坐标前k小的, 然后每次注意从单调栈中取出小于当前查询结果的东西.
做不了一点哈, 考试的时候做不出来一点(心烦意乱)
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define int int
#define uint unsigned int
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=500000;
const int MxNLogN=20*MxN;
const int MxQ=200000;
const int MxM=200000;
int n,m,q;
struct Person{
int lbound;
int rbound;
}P[MxN+5];
int bound[MxM+5];
int root[MxN+5];
bool Comp1(Person u,Person v){
if(u.lbound==v.lbound){
return u.rbound<v.rbound;
}
else{
return u.lbound<v.lbound;
}
}
struct Segment_Tree{
int tot;
int child[MxNLogN+5][2];
int value[MxNLogN+5];
void Insert(int L,int R,int val,int q,int &p){
p=++tot;
child[p][0]=child[q][0];child[p][1]=child[q][1];
value[p]=value[q]+1;
if(L==R){
return;
}
int mid=(L+R)>>1;
if(val<=mid){
Insert(L,mid,val,child[q][0],child[p][0]);
}
else{
Insert(mid+1,R,val,child[q][1],child[p][1]);
}
}
int QuerySum(int L,int R,int k,int q,int p){
if(L==R){
return value[p]-value[q];
}
int mid=(L+R)>>1,sum=value[child[p][1]]-value[child[q][1]];
if(k<=mid){
return QuerySum(L,mid,k,child[q][0],child[p][0])+sum;
}
else{
return QuerySum(mid+1,R,k,child[q][1],child[p][1]);
}
}
int QueryKth(int L,int R,int k,int q,int p){
if(L==R){
return L;
}
int mid=(L+R)>>1,sum=value[child[p][1]]-value[child[q][1]];
if(k<=sum){
return QueryKth(mid+1,R,k,child[q][1],child[p][1]);
}
else{
return QueryKth(L,mid,k-sum,child[q][0],child[p][0]);
}
}
}SGT;
int stk[MxN+5],rem[MxN+5],height[MxN+5],top;
int Solve(){
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&bound[i]);
}
sort(bound+1,bound+m+1);
top=0;
for(int i=1;i<=m;i++){
while(top&&height[top]<bound[i]){
top--;
}
int res=rem[top]+SGT.QuerySum(1,n,bound[i],root[stk[top]],root[bound[i]])-bound[i];
if(res<0){
return false;
}
else if(i==m){
return true;
}
int h=SGT.QueryKth(1,n,res-rem[top],root[stk[top]],root[bound[i]]);
while(top&&h>height[top]){
top--;
h=SGT.QueryKth(1,n,res-rem[top],root[stk[top]],root[bound[i]]);
}
stk[++top]=bound[i];
height[top]=h;
rem[top]=res;
}
return 1;
}
void Main(int Case){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&P[i].lbound,&P[i].rbound);
}
sort(P+1,P+n+1,Comp1);
int pos=1;
for(int i=1;i<=n;i++){
root[i]=root[i-1];
while(pos<=n&&P[pos].lbound==i){
SGT.Insert(1,n,P[pos].rbound,root[i],root[i]);
pos++;
}
}
scanf("%d",&q);
for(int i=1;i<=q;i++){
printf("%d\n",Solve());
}
}
void Initialize(){
}
}
char InputFilename[105]="teams.in" ;
char OutputFilename[105]="teams.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}
D. 路过中丹
题意
询问一个字符串的一个子串是否有偶回文覆盖.
赛时思路
我赛时能想到PAM我觉得已经很可以了,虽然不需要PAM就是了.
算法概述
考虑使用manacher计算最长回文半径,然后可以将询问按左端点离线,使用数据结构扫描线维护右端点是否存在偶回文覆盖即可,注意特判存在奇回文的情况.
简单的代码
/*
* Contest:
* Problem:
* Date:
*/
#include<bits/stdc++.h>
using namespace std;
#define uint unsigned
#define integer long long
#define uinteger unsigned long long
#define Integer __int128_t
#define uInteger __uint128_t
#define ldb long double
#define thi (*this)
#define Maximize(_A_,_B_) (_A_)=(_A_)>(_B_)?(_A_):(_B_)
#define Minimize(_A_,_B_) (_A_)=(_A_)<(_B_)?(_A_):(_B_)
#define Clean(_A_) memset(_A_,0,sizeof(_A_))
#define Copy(_A_,_B_) memcpy(_B_,_A_,sizeof(_B_))
mt19937_64 mt_rand(time(0));
#define rand mt_rand
const int MOD =1000000007;
const int inf =1e9;
const integer infinity =1e18;
const ldb eps =1e-10;
const int DirectX[] ={1,0,-1,0,1,1,-1,-1};
const int DirectY[] ={0,1,0,-1,1,-1,1,-1};
namespace WangManhe{
const int MxN=1000000;
int n,q;
char str[MxN+5];
int prefix[MxN+5],d2[MxN+5],lbound[MxN+5],rbound[MxN+5];
struct query{
int l,r;
int index;
};vector<query>querys[MxN+5];
int Answer[MxN+5];
vector<int>op[MxN+5];
struct Union_Find_Set{
int father[MxN+5];
int Find(int u){
if(father[u]==u){
return u;
}
return father[u]=Find(father[u]);
}
void Union(int u,int v){
int fu=Find(u),fv=Find(v);
father[fu]=fv;
}
}dsu;
struct Binary_Index_Tree{
int T[MxN+5];
void Modify(int p,int c){
for(int i=p;i<=MxN;i+=i&-i){
T[i]+=c;
}
}
int Query(int p){
int res=0;
for(int i=p;i;i-=i&-i){
res+=T[i];
}
return res;
}
void Modify(int l,int r,int c){
Modify(l,c);
Modify(r+1,-c);
}
}bit;
void Manacher(){
int l=0,r=0;
for(int i=1;i<n;i++){
if(i<=r){
d2[i]=min(r-i,d2[l*2-i]);
}
while(str[i-d2[i]]==str[i+d2[i]+1]){
d2[i]++;
}
if(i+d2[i]>r){
l=i;
r=i+d2[i];
}
}
}
void Main(int Case){
int u,v;
scanf("%d%s",&n,str+1);
str[0]='0';
str[n+1]='1';
for(int i=2;i<=n;i++){
prefix[i]=prefix[i-1]+(str[i-1]==str[i+1]);
}
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d%d",&u,&v);
if(prefix[v-1]-prefix[u]>0){
Answer[i]=1;
}
else{
querys[u].push_back({u,v,i});
}
}
Manacher();
for(int i=0;i<=n+1;i++){
dsu.father[i]=i;
rbound[i]=n+1;
}
for(int i=1;i<n;i++){
for(int j=dsu.Find(i-d2[i]+1);j<=i;j=dsu.Find(j)){
rbound[j]=i;
dsu.Union(j,j+1);
}
}
for(int i=0;i<=n+1;i++){
dsu.father[i]=i;
lbound[i]=0;
}
for(int i=n-1;i;i--){
for(int j=dsu.Find(i+1);j<=i+d2[i];j=dsu.Find(j)){
lbound[j]=i;
dsu.Union(j,j+1);
}
}
for(int i=1;i<=n;i++){
if(lbound[i]){
op[lbound[i]*2-i+1].push_back(i);
}
else{
bit.Modify(i,2*rbound[i]-i,1);
}
}
for(int i=1;i<=n;i++){
for(auto o:querys[i]){
Answer[o.index]=(bit.Query(o.r)==0);
}
for(auto o:op[i]){
bit.Modify(o,2*rbound[o]-o,1);
}
bit.Modify(i,2*rbound[i]-i,-1);
}
for(int i=1;i<=q;i++){
printf("%d",Answer[i]);
}printf("\n");
}
void Initialize(){
}
}
char InputFilename[105]="pass.in" ;
char OutputFilename[105]="pass.out";
signed main(){
freopen( InputFilename,"r",stdin );
freopen(OutputFilename,"w",stdout);
signed TotalCase=1;
// scanf("%d",&TotalCase);
WangManhe::Initialize();
for(signed Case=1;Case<=TotalCase;++Case){
WangManhe::Main(Case);
}
return 0;
}