noip前准备日记
noip前准备日记
csp就把它埋了吧。
Day -26
刚考完csp,没有考试。。
刷了几个好题。。
P3076 [USACO13FEB] TaxiG
这个题目硬想是不好想的,但是我们可以分步来做。
首先我们知道每个 \(s_i\) 到 \(t_i\) 的距离一定是一定的。
我们没有办法避免,所以就要先加上这个东西。
之后我们就要送完每个奶牛之后去接下一个。
这个东西是我们需要仔细想的。
每一个奶牛的行程还是相当一条线段,我们现在的主要任务就是将这些线段用最小的代价收尾相接起来。
那么我们就可以将每一个起点的坐标进行排序,然后将每一个终点的坐标进行排序,之后按位相减的一定就是代价最小的答案。
可是这样的话就少掉了终点和起点的路程。
因为我们从起点出发一定会先去找到一个起点去接奶牛,终点之前也一定是一个终点才对。
所以我们就可以把终点塞到起点的排序的数组中,把起点塞到终点的,然后再去排序。
按位相减就能得到最优的答案了。
那么这个结论如何证明呢??
我们如果都进行排序之后,如果任意交换一对数值,那么得到的代价一定会变大。
所以排序后的就一定是最小的。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next.y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
int s[maxn],t[maxn];
int n,m,ans;
inline short main()
{
io >> n >> m;
try(i,1,n) io >> s[i] >> t[i],ans += abs(s[i] - t[i]);
s[n+1] = m; t[n+1] = 0;
std::sort(s+1,s+n+2); std::sort(t+1,t+n+2);
try(i,1,n+1) ans += abs(s[i] - t[i]);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
[NOIP2018 提高组] 赛道修建
首先这个题目问的最大的最小值就能一眼看出来这就是一个二分答案。
那么我们该如何进行check就是问题所在了。
我们首先一定是想要让赛道的数量是尽可能多的。
所以我们如果首先选择离根节点比较近的节点,那么就会损失掉更多的赛道。
所以我们首先递归到最下面开始贪心。
现在就有一个问题,就是我们手上有一个序列,然后我们需要找到最小的值使得两个不同的数 \(x\),\(y\)使得 \(x+y\ge val\),之前考到过一个 meet in middle
的题目自己当时并不知道该如何在小于 \(\mathcal O(n^2)\) 的复杂度下求出这个东西。
然后就学到了二分可以做到,所以我们在一个序列上面进行二分。
但是我们需要判断重复,如果重复,那么将指针加一一定是最优的选择。
之后我们对于没有被选到的边进行传递,显然只能传递一个,那么最大的一定就是最优的情况。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
class xin_edge{public:int next,ver,w;}edge[maxn];
int head[maxn],rp;
inline void add(int x,int y,int z) {edge[++rp].ver = y; edge[rp].next = head[x]; edge[rp].w = z; head[x] = rp;};
int n,m,he = 0,ret;
int pre[maxn],temp[maxn],zhi = 0,vis[maxn];
void dfs(int x,int fa,int val)
{
// jb(x);
go(i,x) if(y != fa) dfs(y,x,val);
zhi = 0;
go(i,x) if(y != fa) temp[++zhi] = pre[y] + edge[i].w;
std::sort(temp+1,temp+zhi+1);
// jb(x);
// try(i,1,zhi) cout<<temp[i]<<' ';
// cout<<endl;
while(temp[zhi] >= val) ret --,zhi --;
// jb(ret);
try(i,1,zhi) if(vis[i] != x)
{
int pos = std::lower_bound(temp+1,temp+zhi+1,val - temp[i]) - temp;
// sb(i);sb(val - temp[i]);jb(pos);
while(pos <= zhi and (vis[pos] == x or pos == i)) pos ++;
// jb(pos);
if(pos <= zhi) vis[pos] = vis[i] = x,ret --;
}
throw(i,zhi,1) if(vis[i] != x) {pre[x] = temp[i]; break;}
}
inline int check(int x)
{
memset(vis,0,sizeof(int) * (n + 1));
memset(pre,0,sizeof(int) * (n + 1));
ret = m;
dfs(1,0,x);
return ret <= 0;
}
inline short main()
{
io >> n >> m;
try(i,1,n-1)
{
register int x,y,z; io >> x >> y >> z;
add(x,y,z); add(y,x,z); he += z;
}
// go(i,1) jb(y);
int l = 0,r = he,ans = 0;
// jb(r);
// cout<<check(7987)<<endl;
// return 0;
while(l <= r)
{
register int mid = l + r >> 1;
if(!check(mid)) r = mid - 1;
else l = mid + 1,ans = mid;
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
[CSP-S 2021] 廊桥分配
纪念一下爆炸的csp吧。
思路是对的,但是确实实现假掉了,最后20min什么其实也救不回来,顶多就是一个文件名什么的。
首先我们一定会有一个 \(40pts\) 的 \(\mathcal O(n^2log)\) 的做法。
然后我删了
就是对于每一种分配方式然后 \(\mathcal O(nlog)\) 进行 \(check\)
但是呢,这个不够优秀。
所以我们考虑如何优化 \(check\)。
我们其实可以挨个考虑贡献,我们首先进行一遍预处理。
也其实就是模拟这个过程一遍,然后我们就可以发现在当时有 \(x\) 个廊桥被占有的时候,就会对 \(x\)~\(n\) 的答案造成 \(1\) 的贡献。
那么这其实就可以使用树状数组来维护。
那么如何预处理呢。
这就是我考场上没掉的地方,就是一个顺序的问题。
因为粗心,考场上并没有看到这个东西每一个坐标都是独一无二的。
然后就直接在优先队列里面按照 \(r\) 排序了。
其实这样并不是对的,一般的数据都会把我杀死,但是infoj给了我30
所以其实离散化一下每一个位置只有一个变化,这样使用一个普通的小跟堆就好了。
也确实,当时想要搏一把,20min属实啥也调不对。
但是没有打组合拳属实不应该啊。
菜就是菜了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next.y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
namespace xin
{
int n,m1,m2;
class xin_bit
{
private:
#define low(x) (x & -x)
public:
int c[maxn];
inline void add(int x,int val) {for(;x<=n;x+=low(x)) c[x] += val;}
inline int query(int x) {int ret = 0; for(;x;x-=low(x)) ret += c[x]; return ret;}
}bit1,bit2;
class xin_data
{
public:
int l,r;
xin_data(){}
xin_data(int l,int r):l(l),r(r){}
}d1[maxn],d2[maxn];
int lisan1[maxn],cnt1 = 0,cnt2 = 0;
int lisan2[maxn];
xin_data pos1[maxn],pos2[maxn];
std::priority_queue<int,std::vector<int>,std::greater<int>>q1,q2;
int vis1[maxn],vis2[maxn];
inline short main()
{
io >> n >> m1 >> m2;
try(i,1,m1) io >> d1[i].l >> d1[i].r,lisan1[++cnt1] = d1[i].l,lisan1[++cnt1] = d1[i].r,q1.push(i);
try(i,1,m2) io >> d2[i].l >> d2[i].r,lisan2[++cnt2] = d2[i].l,lisan2[++cnt2] = d2[i].r,q2.push(i);
std::sort(lisan1+1,lisan1+cnt1+1); std::sort(lisan2+1,lisan2+cnt2+1);
try(i,1,m1)
{
d1[i].l = std::lower_bound(lisan1+1,lisan1+cnt1+1,d1[i].l) - lisan1;
d1[i].r = std::lower_bound(lisan1+1,lisan1+cnt1+1,d1[i].r) - lisan1;
pos1[d1[i].l] = xin_data(i,0); pos1[d1[i].r] = xin_data(i,1);
}
try(i,1,m2)
{
d2[i].l = std::lower_bound(lisan2+1,lisan2+cnt2+1,d2[i].l) - lisan2;
d2[i].r = std::lower_bound(lisan2+1,lisan2+cnt2+1,d2[i].r) - lisan2;
pos2[d2[i].l] = xin_data(i,0); pos2[d2[i].r] = xin_data(i,1);
}
// try(i,1,m1) sb(d1[i].l),jb(d1[i].r);
try(i,1,2*m1)
{
if(pos1[i].r == 0) vis1[pos1[i].l] = q1.top(),bit1.add(q1.top(),1),q1.pop();
else q1.push(vis1[pos1[i].l]);//,jb(vis1[pos1[i].l]);
}
try(i,1,2*m2)
{
if(pos2[i].r == 0) vis2[pos2[i].l] = q2.top(),bit2.add(q2.top(),1),q2.pop();
else q2.push(vis2[pos2[i].l]);
}
int ans = 0;
try(i,0,n)
ans = std::max(ans,bit1.query(i) + bit2.query(n-i));
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
[NOIP2014 提高组] 联合权值
似乎感觉在哪里做过啊。
然后发现确实做过,然后那个是一个改编的题目。
暴力的思路比较一样,但是实际上正解并不是一样的。
这个题目首先暴力应该是最差应该只有 \(30pts\),但是没想到当时 \(CCF\) 的数据那么友善,一直猛给部分分数。
然后我就有 \(70pts\),然后开始考虑正解。
因为我们考虑的是距离为 \(2\) 的,所以一定会有一个中专的点。
但是问题就是知道这个点了怎么做?
考虑最最朴素的算法,也就是暴力算,式子就是:
那么其实就是
所以就可以很快算出来了。
至于最大值,那么其实就是每一个中转点连接的最大值和次大值的乘积。
之后我总感觉这个的时间复杂度不是很对,但是似乎跑的飞快。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
const int mod = 10007;
std::vector<int>vec[maxn/5];
int n;
int w[maxn];
int ans1,ans2;
inline short main()
{
io >> n;
try(i,1,n-1)
{
register int x,y; io >> x >> y;
vec[x].push_back(y); vec[y].push_back(x);
}
try(i,1,n) io >> w[i];
try(i,1,n)
{
int he1 = 0,he2 = 0;
int maxx1 = 0,maxx2 = 0;
for(auto y : vec[i])
{
if(w[y] > maxx1) maxx2 = maxx1,maxx1 = w[y];
else if(w[y] > maxx2) maxx2 = w[y];
he1 += w[y];
(he2 += w[y] * w[y]) %= mod;
}
he1 = he1 * he1 % mod;
ans1 = std::max(ans1,maxx1 * maxx2);
(ans2 += he1 - he2 + mod) %= mod;
}
cout<<ans1<<' '<<ans2<<endl;
return 0;
}
}
signed main() {return xin::main();}
CF702C Cellular Network
就是一个大大大大水题。
然后看错题目然后调了半天。
生气。
就是计算每一个的前驱后继,然后取最小值的最大值就行了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
//#define int long long
namespace xin
{
int n,m,ans;
int a[maxn],b[maxn],pi[maxn];
int pai1[maxn],pai2[maxn];
int pos1[maxn],pos2[maxn];
int ner1[maxn],ner2[maxn];
bool vis[maxn];
inline short main()
{
io >> n >> m;
try(i,1,n) io >> a[i]; try(i,1,m) io >> b[i],pai1[i] = pai2[i] = b[i],pai2[i] = -pai2[i];//,jb(pai2[i]);
std::sort(pai1+1,pai1+m+1); std::sort(pai2+1,pai2+m+1);
// jb(std::lower_bound(pai2+1,pai2+m+1,-17) - pai2);
// try(i,1,m) sb(pai1[i]),jb(pai2[i]);
try(i,1,n)
{
pos1[i] = std::lower_bound(pai1+1,pai1+m+1,a[i]) - pai1,pos2[i] = std::lower_bound(pai2+1,pai2+m+1,-a[i]) - pai2;
pos2[i] = m - pos2[i] + 1;
if(!pos1[i] or pos1[i] == m + 1) ans = std::max(ans,abs(a[i] - pai1[pos2[i]]));
else if(!pos2[i] or pos2[i] == m + 1) ans = std::max(ans,abs(a[i] - pai1[pos1[i]]));
else ans = std::max(ans,std::min(abs(a[i] - pai1[pos1[i]]),abs(a[i] - pai1[pos2[i]])));
// sb(pos1[i]); sb(pos2[i]); jb(ans);
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day -25
今天又开始联考了。
开始感觉上来是一个很普通的大水题。。
然后认为线段树直接就是可以过掉这个题目。
然后发现范围。。。
淦。
然后开始疯狂想如何 \(\mathcal O(n)\) 做。
发现是一堆的线段,然后认为可以差分,然而似乎并不知道该如何差分。
但是脑袋当中就一直出现带着 \(log\) 的做法。
感觉似乎只有 \(60pts\),鉴于csp的经验,因为时间比较充足,所以打上了对拍
在打 pai.cpp
的时候,突然发现似乎这个暴力的复杂度是均摊一个 \(n\) 的,然后试着证明了一下,发现确实。
然后就反过来了,用着自己的线段树拍着暴力,发现暴力跑极限只有
\(0.7s\),感觉很好。
开 \(T2\) ,发现手头没有小于 \(\mathcal O(n^3)\) 的做法,感觉不是很好。。
然后就写了一个 \(\mathcal O(n^3)\) 的,然后写了一个菊花图的部分分数。
但是如果只要我找到 \(\mathcal O(n^2log)\) 的做法,那么我就可以解决掉链的部分和一个询问的部分。
这样就有足足 \(80pts\),然而失败了。
\(T3\) 和 \(T4\) 都是写了一个极端弱智的部分分数走人了。
然后就是 \(100+45+25+10\)
树上的数
这个名字似乎像是一个原题,但是完全不是。
做法很简单,但是不太容易想到去证明这个玩意的复杂度。
并且极端卡常。
我也不知道为啥我这么快
直接每一个询问进行 \(dfs\),遇到已经贡献过的就停下来就好了。
只有 \(n\) 个节点,所以一定就是 \(\mathcal O(n)\) 的。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for( int i=a;i<=b;++i)
#define throw(i,a,b) for( int i=a;i>=b;--i)
#define go(i,x) for( signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 5e6+10,inf = 1e9+10;
// #define int long long
namespace xin
{
int n,m;
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],rp;
auto add = [](int x,int y){edge[++rp].ver = y; edge[rp].next = head[x]; head[x] = rp;};
int fa[maxn];
int a,b,he;
int q[maxn],tot,id[maxn],siz[maxn];
bool vis[maxn];
void run(int x)
{
if(!vis[x]) return ;
he += vis[x]; vis[x] = 0;
go(i,x) run(y);
}
inline short main()
{
file(tree);
io >> n >> m;
io >> a >> b;
fa[2] = 1; add(1,2); vis[1] = vis[2] = 1;
try(i,3,n) fa[i] = ((1ll * fa[i - 1] * a + b) ^ 19760817) % (i - 1) + 1,add(fa[i],i),vis[i] = 1;
io >> q[1] >> a >> b;
int ans;
run(q[1]);
ans = n - he;
// sb(siz[q[1]]); jb(he);
try(i,2,m)
{
q[i] = 1ll * (((1ll * q[i - 1] * a + b) ^ 19760817) ^ (i << 1)) % (n - 1) + 2;
run(q[i]);
ans ^= n - he;
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
时代的眼泪
其实这个题目的突破就是 \(\mathcal O(n)\) 的做法。
我们现将权值进行离散化,之后建立权值树状数组。
我们先从 \(1\) 节点开始 \(dfs\),之后到达一个节点就变成一个节点的状态,回溯就好了。
具体就是到达一个节点,然后就将 \(1\) ~ \(w_x-1\) 的下标增加一个 \(1\)。
这就是对下面的点的贡献。
这样就能 \(nlog\) 求出来一个的了。
然后的关键就是在换根 \(dp\) 上面,我们有一个之后,其他的答案自然就很好从那个来转移了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10;
#define int long long
namespace xin
{
class xin_edge{public:int next,ver,w;}edge[maxn];
int head[maxn],rp;
inline void add(int x,int y) {edge[++rp].ver = y; edge[rp].next = head[x]; head[x] = rp;}
int n,qnum,w[maxn];
class xin_bit
{
private:
#define low(x) (x & -x)
public:
int c[maxn];
inline void add(int x,int val) {for(;x<=n;x+=low(x)) c[x] += val;}
inline int query(int x) {int ret = 0; for(;x;x-=low(x)) ret += c[x]; return ret;}
}bit;
int lisan[maxn],cnt = 0;
int ret;
int f[maxn],dp[maxn];
void dfs(int x,int fa)
{
bit.add(w[x],1);
int temp = bit.query(w[x]-1),pre = temp;
go(i,x) if(y != fa)
{
dfs(y,x);
int now = bit.query(w[x]-1);
edge[i].w = now - pre; pre = now;
}
f[x] = bit.query(w[x]-1) - temp;
dp[1] += f[x];
// bit.add(w[x]-1,-1);
}
void get_ans(int x,int fa)
{
go(i,x) if(y != fa)
{
dp[y] = dp[x] - edge[i].w + bit.query(w[y]-1) - f[y];
get_ans(y,x);
}
}
inline short main()
{
file(tears);
io >> n >> qnum;
try(i,1,n) io >> w[i],lisan[++cnt] = w[i];
std::sort(lisan+1,lisan+cnt+1);
int k = std::unique(lisan+1,lisan+cnt+1) - (lisan + 1);
try(i,1,n) w[i] = std::lower_bound(lisan+1,lisan+k+1,w[i]) - lisan;
try(i,1,n-1)
{
register int x,y; io >> x >> y;
add(x,y); add(y,x);
}
dfs(1,0);
get_ans(1,0);
try(i,1,qnum)
{
register int x; io >> x;
printf("%lld\n",dp[x]);
}
return 0;
}
}
signed main() {return xin::main();}
传统艺能
本质不同的子序列的个数的这个东西还是没有记住啊。
意义就是对于每一个以 \(c\) 结尾的子序列的个数为 \(f_c\),然后这个东西就是表示在每一种字符结尾后面再去接一个新的字符,之后在加上自己一个的新字符就是所有的情况了。
所以这个玩意可以矩阵快速幂
设置三个矩阵,然后修改在线段树上面修改,注意矩阵不满足交换律。
1 | |||
---|---|---|---|
1 | 1 | ||
1 | 1 | ||
1 | 1 |
1 | 1 | ||
---|---|---|---|
1 | |||
1 | 1 | ||
1 | 1 |
1 | 1 | ||
---|---|---|---|
1 | 1 | ||
1 | |||
1 |
之后向量的 a[1][1] + a[1][2] + a[1][3]
就是答案了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gec() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scnaf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
#define int long long
namespace xin
{
const int mod = 998244353;
char s[maxn],in[10];
int a[maxn],f[maxn];
bool sp = 1;
class xin_mat
{
public:
int a[5][5];
xin_mat(){}
inline void out()
{
try(i,1,4)
{
try(j,1,4)
cout<<a[i][j]<<' ';
cout<<endl;
}
cout<<endl;
}
// xin_mat(int n,int m):n(n),m(m){}
friend xin_mat operator * (const xin_mat &x,const xin_mat &y)
{
xin_mat ret;
// x.out(); y.out();
// try(i,1,4)
// {
// try(j,1,4)
// cout<<x.a[i][j]<<' ';
// cout<<endl;
// }
// try(i,1,4)
// {
// try(j,1,4)
// cout<<y.a[i][j]<<' ';
// cout<<endl;
// }
memset(ret.a,0,sizeof(ret.a));
try(i,1,4) try(j,1,4) try(k,1,4)
(ret.a[i][j] += x.a[i][k] * y.a[k][j]) %= mod;
// jb(ret.a[1][1]);
// try(i,1,4)
// {
// try(j,1,4)
// cout<<ret.a[i][j]<<' ';
// cout<<endl;
// }
return ret;
}
}base,ch[5],vec;
int n,m;
class xin_seg
{
private:
#define ls(fa) (fa << 1)
#define rs(fa) (fa << 1 | 1)
#define up(fa) (t[fa].s = t[ls(fa)].s * t[rs(fa)].s)
public:
class xin_tree{public:xin_mat s;}t[maxn];
void build(int fa,int l,int r)
{
if(l == r) return t[fa].s = ch[a[l]],void();
register int mid = l + r >> 1;
build(ls(fa),l,mid); build(rs(fa),mid+1,r);
up(fa);
// t[fa].s.out();
}
void upd(int fa,int l,int r,int pos,int val)
{
if(l == r) return t[fa].s = ch[val],void();
register int mid = l + r >> 1;
if(pos <= mid) upd(ls(fa),l,mid,pos,val);
else upd(rs(fa),mid+1,r,pos,val);
up(fa);
}
xin_mat query(int fa,int l,int r,int ql,int qr)
{
if(ql <= l and qr >= r) return t[fa].s;
register int mid = l + r >> 1,ok = 0; xin_mat ret;
if(ql <= mid) ret = query(ls(fa),l,mid,ql,qr),ok = 1;
if(qr > mid)
{
if(ok) ret = ret * query(rs(fa),mid+1,r,ql,qr);
else ret = query(rs(fa),mid+1,r,ql,qr);
}
return ret;
}
}t;
inline void init()
{
try(i,1,3) memset(ch[i].a,0,sizeof(ch[i].a));
// ch[1] = ch[2] = ch[3] = xin_mat(4,4);
ch[1].a[1][1] = ch[1].a[2][1] = ch[1].a[3][1] = ch[1].a[4][1] = ch[1].a[2][2] = ch[1].a[3][3] = ch[1].a[4][4] = 1;
ch[2].a[1][1] = ch[2].a[1][2] = ch[2].a[2][2] = ch[2].a[3][2] = ch[2].a[3][3] = ch[2].a[4][2] = ch[2].a[4][4] = 1;
ch[3].a[1][1] = ch[3].a[2][2] = ch[3].a[3][3] = ch[3].a[4][4] = ch[3].a[1][3] = ch[3].a[2][3] = ch[3].a[4][3] = 1;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(string);
#endif
io >> n >> m;
scanf("%s",s+1);
try(i,1,n)
{
a[i] = s[i] - 'A' + 1;
if(a[i] != 1) sp = 0;
}
memset(base.a,0,sizeof(base.a)); memset(vec.a,0,sizeof(vec.a));
try(i,1,4) base.a[i][i] = 1;
vec.a[1][4] = 1;
init();
// try(cse,1,3)
// {
// try(i,1,4)
// {
// try(j,1,4)
// cout<<ch[cse].a[i][j]<<' ';
// cout<<endl;
// }
// cout<<endl;
// }
t.build(1,1,n);
try(cse,1,m)
{
register int op; io >> op;
if(op == 1)
{
register int x; io >> x;
char in[10]; scanf("%s",in+1);
a[x] = in[1] - 'A' + 1;
t.upd(1,1,n,x,a[x]);
}
else
{
register int l,r; io >> l >> r;
xin_mat ret = t.query(1,1,n,l,r),temp = vec;
temp = temp * ret;
printf("%lld\n",(temp.a[1][1] + temp.a[1][2] + temp.a[1][3]) % mod);
}
}
return 0;
}
}
signed main() {return xin::main();}
铺设道路
贪心。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
#define int long long
namespace xin
{
const int mod = 1e9+7;
std::queue<int>q;
std::stack<int>st;
int n,a[maxn],b[maxn];
int ans1,ans2,ans;
inline short main()
{
#ifdef ONLINE_JUDGE
file(road);
#endif
io >> n;
try(i,1,n) io >> a[i];
try(i,1,n+1) b[i] = a[i] - a[i-1],ans += (b[i] > 0) ? b[i] : 0;
try(i,1,n+1)
{
if(b[i] > 0) q.push(i);
else
{
while(b[i] < 0)
{
register int x = q.front();
if(abs(b[x]) <= abs(b[i]))
{
b[i] += b[x];
ans1 = (ans1 + (i - x) * (i - x) % mod * b[x] % mod) % mod;
q.pop();
}
else
{
b[x] += b[i];
ans1 = (ans1 + (i - x) * (i - x) % mod * (-b[i]) % mod) % mod;
b[i] = 0;
}
}
}
}
try(i,1,n+1) b[i] = a[i] - a[i-1];
try(i,1,n+1)
{
if(b[i] > 0) st.push(i);
else
{
while(b[i] < 0)
{
register int x = st.top();
if(abs(b[x]) < abs(b[i]))
{
b[i] += b[x];
ans2 = (ans2 + (i - x) * (i - x) % mod * b[x] % mod) % mod;
st.pop();
}
else
{
b[x] += b[i];
ans2 = (ans2 + (i - x) * (i - x) % mod * (-b[i]) % mod) % mod;
b[i] = 0;
}
}
}
}
cout<<ans<<endl<<ans2<<endl<<ans1<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day -24
昨天在做 \(T2\) 的时候,发现自己的树状数组的理解是偏差的。
所以就重新学了一遍树状数组,发现其实树状数组的基本是单点修改,区间查询
我一直认为是区间修改,单点查询啊。。
只不过区间修改可以使用差分来解决。
树状数组还有一个强大的用处,就是进行区间修改,区间查询
感觉很强,实际更强。
无论是空间上面还是时间上面又要优过线段树。
这个其实也是一个差分的过程。
所以就可以维护两个差分的数组,然后在查询的时候 (p + 1) * c1[i] - c2[i]
就好了。
code
class xin_bit
{
private:
#define low(x) (x & -x)
public:
int c1[maxn],c2[maxn];
inline void add(int x,int val)
{
for(int i=x;i<=n;i+=low(i)) c1[i] += val,c2[i] += x * val;
}
inline void upd(int l,int r,int val) {add(l,val); add(r+1,-val);}
inline int ask(int x)
{
int ret = 0;
for(int i=x;i;i-=low(i))
ret += (x + 1) * c1[i] - c2[i];
return ret;
}
inline int query(int l,int r) {return ask(r) - ask(l-1);}
}bit;
还是很快的。
然后开始考试啊。
几天的考试颇有几番不顺利。
上来的思路感觉修正很对,然后飞速写完,一下子过了小样例。
然后测大样例。。。
发现两百个里面有两个是不一样的,心态稍炸。
然后疯狂调试,发现做法是假的,但是认为 \(q=1\) 的点是可以过去的。
所以就放下了。
然后开始做 \(T2\),然后认为很垃圾的一个题目。
然后。。。
小样例又过了。
大样例。。。还是错两个??!!
然后又没了。
最后两个题目的 \(pretask\) 都没有过掉。。。
然后后面两个题目的东西的暴力疯狂挼了一下。
然后。。。\(30+20+20=70\)
垃圾分数。
宝藏
首先我们要分析出一个很妙的性质,就是说这个的答案是随 \(x\) 的增大而减小的。
那么就可以二分出来答案。
提前两种的排序,然后就是 \(qnlogn\) 的复杂度
如果仔细的话,你会发现这个子任务 \(2\) 并没有给 \(Q\) 的范围。
是不是他忘了呢???
不是,这个真的就是 \(3*10^5\)
所以这个东西只有 \(50pts\) 。
我们考虑正解,我们回想自己在 \(check\) 的过程中,我们分开左边和右边。
我们会分别对左右进行排序。
那么我们不妨把每一种答案都预处理出来,因为对于相邻的询问,变化还是比较小的。
所以两边就可以维护一个 \(set\),之后就可以 \(log\) 转移。
然后我才知道平常一直不让用的关键字 next
这个函数的用法
我们维护 \(ls,rs,mid1,mid2\) 分别表示左边的时间和,右边的时间和,还有左边中间的,右边中间的。
之后就能转移到我们想要的答案了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
class xin_data
{
private:
friend bool operator < (xin_data x,xin_data y)
{
if(x.tim xor y.tim) return x.tim < y.tim;
return x.id < y.id;
}
friend bool operator <= (xin_data x,xin_data y)
{
if(x.tim xor y.tim) return x.tim < y.tim;
return x.id <= y.id;
}
public:
int val,tim,id;
xin_data(){}
xin_data(int val,int tim,int id):val(val),tim(tim),id(id){}
}d[maxn];
int n,qnum,t;
std::set<xin_data>s1,s2;
xin_data mid1,mid2;
int ans[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(treasure);
#endif
io >> n >> t >> qnum;
try(i,1,n) io >> d[i].val >> d[i].tim,d[i].id = i;
std::sort(d+1,d+n+1,[](xin_data x,xin_data y){return (x.val == y.val) ? x.id < y.id : x.val < y.val;});
try(i,1,n-1) s1.insert(d[i]);
s1.insert(mid1); s2.insert(mid2);
int ls = 0,rs = 0,zhi = n;
bool ok = 0;
for(int i=1;i<=n;i+=2)
{
if(ok) {ans[i] = -1; continue;}
while(ls + rs + d[zhi].tim > t and zhi)
{
s2.insert(d[zhi]);
auto it = s2.find(mid2);
if(d[zhi] <= mid2) rs += d[zhi].tim - mid2.tim,mid2 = *--it;
zhi --;
it = s1.find(mid1);
if(d[zhi] <= mid1)
{
if(++it != s1.end()) mid1 = *it;
else {ok = 1; break;}
ls += mid1.tim - d[zhi].tim;
}
s1.erase(d[zhi]);
}
ans[i] = d[zhi].val;
if(ok) {ans[i] = -1; continue;}
auto it = s2.find(mid2);
if(++it == s2.end())
{
s2.insert(d[zhi]);
auto it = s2.find(mid2);
if(d[zhi] <= mid2) rs += d[zhi].tim - mid2.tim,mid2 = *--it;
zhi --;
it = s1.find(mid1);
if(d[zhi] <= mid1)
{
if(++it != s1.end()) mid1 = *it;
else {ok = 1; break;}
ls += mid1.tim - d[zhi].tim;
}
s1.erase(d[zhi]);
}
it = s2.find(mid2); mid2 = *next(it),rs += mid2.tim;
it = s1.find(mid1); if(next(it) != s1.end()) mid1 = *next(it),ls += mid1.tim; else ok = 1;
}
try(cse,1,qnum)
{
register int x; io >> x;
printf("%lld\n",ans[x]);
}
return 0;
}
}
signed main() {return xin::main();}
寻找道路
考场上面想到了正解,但是因为细节没有调出来。。。
这个题目实际上就是一个广搜,是一个分布广搜这个是我自己起的名字。。。
然后注意 \(0\) 和 \(1\) 的转移顺序,这样的话队列的第一个一定就是最优的答案。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 1e9+7;
class xin_edge{public:int next,ver,w;}edge[maxn];
int head[maxn],rp;
inline void add(int x,int y,int z){edge[++rp].ver = y; edge[rp].next = head[x];edge[rp].w = z; head[x] = rp;}
int n,m;
bool vis[maxn];
int a[maxn],b[maxn];
std::vector<int>ans[maxn/10],vec;
class xin_data
{
public:
int x,tim;
xin_data(){}
xin_data(int x,int tim):x(x),tim(tim){}
};
int dis[maxn],d[maxn];
std::queue<int>q;
void dfs(int x)
{
vis[x] = 1; q.push(x); dis[x] = 0;
go(i,x) if(!vis[y] and !edge[i].w) dfs(y);
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(path);
#endif
io >> n >> m;
try(i,1,m)
{
register int x,y,z; io >> x >> y >> z;
add(x,y,z);
}
memset(dis,-1,sizeof(int) * (n + 1));
dfs(1);
// debug;
int now1,now2;
while(q.size())
{
int zhi = 0; now1 = d[q.front()]; now2 = dis[q.front()];
while(q.size() and d[q.front()] == now1 and dis[q.front()] == now2) a[++zhi] = q.front(),q.pop();
try(i,1,zhi)
{
int x = a[i];
go(i,x) if(!vis[y] and !edge[i].w)
{
d[y] = d[x] + 1;
dis[y] = dis[x] * 2 % mod;
q.push(y); vis[y] = 1;
}
}
try(i,1,zhi)
{
int x = a[i];
go(i,x) if(!vis[y] and edge[i].w)
{
d[y] = d[x] + 1;
dis[y] = (dis[x] * 2 + 1) % mod;
q.push(y); vis[y] = 1;
}
}
}
try(i,2,n) printf("%lld ",dis[i]);
return 0;
}
}
signed main() {return xin::main();}
考试总结
这场考试实际上做的很差,在时间分配问题上还有很大的问题,主要在第一个题目上面的用时其实还是不够,总是感觉会影响后面的题目,但是其实只有第一个题目和第二个题目是比较可做的。
其实应该敢用时间在读题目上面,可以开始的时候先把左右的题目通读一遍,之后可以根据估计的难度进行时间的分配,可以先把认为非常不可做的题目的暴力打完,这样心里面就没有了负担。
之后再去花费比较大量的时间在自己有可能做出来的题目上面。
这样就能将自己会做的题目尽可能的拿上最多的分数了。
Day -23
今天刚上来就有一个口胡比赛,结果一番口胡正解。。。
红黑树
首先这个每一次的操作永远是以颜色相同的联通块为前提的。
所以我们看这个树的最小单位就应该是一个一个的联通块。
那么就可以开始缩点,把颜色相同的联通块缩成一个点。
之后考虑如何将这个树以最小的操作数量变成一个颜色。
现在的树上的节点每一个都是互不相同的,也就是每隔一个一种颜色。
假设现在是一个这样的树(缩点之后的树),那么这个树的最长的链就是 \(15...1...3...........9...10\) 这个链。
假设我们首先没有操作这个直径上面的节点。
那么直径上面一定还会留下没有被操作过的节点需要被操作。
这样的操作数量一定不会比直径的长度的二分之一要小。
如果我们直接对于直径每隔一个节点进行操作。
那么因为直径无论在哪一个节点后面都是不短的,那么其他所有的子树一定也会在直径操作结束之前被操作完成。
所以答案一定也就是(直径+1)/2
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],zhi;
inline void add(int x,int y) {edge[++zhi].ver = y ;edge[zhi].next= head[x]; head[x] = zhi;}
class xin_data{public:int x,y;}d[maxn];
class xin_tim
{
public:
int x,tim;
xin_tim(){}
xin_tim(int x,int tim):x(x),tim(tim){}
};
int a[maxn],n;
int fa[maxn];
int biao[maxn],tot;
bool vis[maxn];
inline int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
void dfs(int x,int f)
{
go(i,x) if(y != f)
{
dfs(y,x);
if(a[y] == a[x]) fa[find(x)] = find(y);
}
}
inline short main()
{
io >> n;
try(i,1,n) io >> a[i],fa[i] = i;
try(i,1,n-1)
{
io >> d[i].x >> d[i].y;
// add(x,y); add(y,x);
add(d[i].x,d[i].y); add(d[i].y,d[i].x);
}
dfs(1,0);
// try(i,1,n) sb(i),jb(find(i));
try(i,1,n)
{
int fx = find(i);
if(!vis[fx])
{
vis[fx] = 1;
biao[fx] = ++tot;
}
}
// try(i,1,n) sb(i),jb(biao[find(i)]);
memset(head,0,sizeof(int) * (n + 1)); zhi = 0;
try(i,1,n-1)
{
int fx = find(d[i].x),fy = find(d[i].y);
if(fx xor fy) add(biao[fx],biao[fy]),add(biao[fy],biao[fx]);//,cout<<(biao[fy])<<' '<<(biao[fx])<<endl;
}
std::queue<xin_tim>q; q.push(xin_tim(1,0)); int pot,now = 0;
memset(vis,0,sizeof(bool) * (n + 1));vis[1] = 1;
while(q.size())
{
xin_tim x = q.front(); q.pop();
// sb(x.tim);jb(x.x);
if(x.tim > now) now = x.tim,pot = x.x;
go(i,x.x) if(!vis[y])
{
vis[y] = 1; q.push(xin_tim(y,x.tim+1));
}
}
// jb(pot);
// jb(now);
q.push(xin_tim(pot,0)); now = 0;
memset(vis,0,sizeof(bool ) * (n + 1)); vis[pot] = 1;
while(q.size())
{
xin_tim x = q.front(); q.pop();
if(x.tim > now) now = x.tim;
go(i,x.x) if(!vis[y]) vis[y] = 1,q.push(xin_tim(y,x.tim+1));
}
// jb(now);
// if(now == 1) cout<<0<<endl;
cout<<(now+1)/2<<endl;
return 0;
}
}
signed main() {return xin::main();}
话说我好像把昨天写的丢了。。
Day -22
我记错了。。。
Day -21
话说为什么昨天还是-23
似乎我从刚开始就记错了。。。
今天还是有模拟赛的一天,没有什么口胡比赛。。
并且还是毒瘤出题人 \(JKLover\) 的sb好题
莓良心
这个其中很关键的一步就是求集合的划分。
这个需要用到第二类斯特林数
但是这个东西到考场上却忘记了怎么推了。。
这个就是方案就是斯特林嘛。。
那怎么求呢??
。。。。。
顺带复习一下子。
\(\begin {Bmatrix} n \\ m\end {Bmatrix}\) 就表示有 \(n\) 个数,划分为 \(k\) 个非空子集的方案数。
那么这个东西如何在小于 xin_team
的时间复杂度求出呢??
首先有一个 \(n^2\) 的递推。
然后初值就是 \(\begin {Bmatrix} n \\ 0 \end {Bmatrix}=[n=0]\)
当然还有通项公式:
似乎是通过一个容斥得出的。
只不过这玩意是 \(nlog\) 的。。
还有一个性质,似乎感觉自己也是背不过。。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define scanf ak = scank
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int a[maxn];
int n,k;
int he;
int s1,s2;
int fac[maxn],inv[maxn];
auto C = [](int n,int m) {return fac[n] * inv[m] % mod * inv[n-m] % mod;};
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod;y >>= 1;
}
return ret;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(ichigo);
#endif
io >> n >> k;
try(i,1,n) io >> a[i],he += a[i],he %= mod;
fac[0] = inv[0] = 1;
try(i,1,k) fac[i] = fac[i-1] * i % mod;
inv[k] = ksm(fac[k],mod-2);
throw(i,k-1,1) inv[i] = inv[i+1] * (i + 1) % mod;
// jb(C(2,1));
int base = 1;
try(i,0,k)
{
(s1 += base * C(k,i) % mod * ksm(k-i,n) % mod + mod) %= mod;
// jb(s1);
(s2 += base * C(k,i) % mod * ksm(k-i,n-1) % mod + mod) %= mod;
// jb(s2);
base = -base;
}
// jb(s1); jb(s2);
cout<<he * ((s1 + (n - 1) * s2 % mod) % mod) % mod * inv[k] % mod<<endl;
return 0;
}
}
signed main() {return xin::main();}
``
</details>
**尽梨了**
这个主要的问题还是在 $dp$ 上面,如果没有进行排序,那么这个需要的 $dp$ 就会是从这些集合当中不重复的选择。
似乎这个只有背包可以完成,但是体积直接达到了 $10^9$
所以正确的解法应该是消除一个的限制。
我们首先明确哪个顺序一定是优的。
之后再进行 $dp$ 才可。
<details>
<summary>code</summary>
```cpp
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
class xin_data
{
public:
int x,y;
xin_data(){}
xin_data(int x,int y):x(x),y(y){}
}d[maxn];
int a[maxn];
int n,tim;
int cnt1,cnt2;
int f[maxn][33];
inline bool comp(const xin_data &x,const xin_data &y) {return x.y * y.x < x.x * y.y;}
inline short main()
{
#ifdef ONLINE_JUDGE
file(eriri);
#endif
io >> n >> tim;
try(i,1,n)
{
register int x,y; io >> x >> y; y += x + 1;
if(!x) a[++cnt2] = y;
else d[++cnt1] = xin_data(x,y);
}
std::sort(d+1,d+cnt1+1,comp); std::sort(a+1,a+cnt2+1);
memset(f,0x3f,sizeof(f)); f[0][0] = 0;
// try(i,1,cnt1) sb(d[i].x),jb(d[i].y);
try(i,0,cnt1-1) try(j,0,32) if(f[i][j] <= tim)
{
f[i+1][j] = std::min(f[i+1][j],f[i][j]);
int temp = (f[i][j] + 1) * (d[i+1].x + 1) + (d[i+1].y - d[i+1].x - 1);
// jb(temp);
if(temp <= tim) f[i+1][j+1] = std::min(f[i+1][j+1],temp);
}
int pos = 0,ans = 0,he = 0;
throw(i,32,0) if(f[cnt1][i] <= tim)
{
while(pos != cnt2 and he + f[cnt1][i] + a[pos+1] <= tim)
he += a[++pos];
ans = std::max(ans,pos + i);
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
团不过
考虑在限定每堆石子数目互不相同的前提下,用所有方案数减去先手必败的方
案数。
设 \(p(i) = (2n −1)^{\underline i}\) ,即 \(i\) 堆石子的总方案数。
设 \(f(i)\) 表示 \(i\) 堆石子时先手必败的方案数。
我们考虑让前 \(i −1\) 堆石子任意取,通过调整最后一堆石子的数目使得异或和为\(0\) ,方案数为 \(p(i −1)\) 。
若前 \(i −1\) 堆石子异或和为 \(0\) ,因为最后一堆不能取 \(0\) ,这种情况是不合法的,
方案数为 \(f(i −1)\) 。
若前 \(i −1\) 堆石子中,有 \(i −2\) 堆石子异或起来是 \(0\) ,那么最后一堆石子就只能
和另一堆石子数目相同,也是不合法的,方案数为 \((i −1) ·(2n −i + 1) ·f(i −2)\) 。
于是得到 \(f(i) = p(i −1) −f(i −1) −(i −1) ·(2n −i + 1) ·f(i −2)\) ,边界为
\(f(1) = f(2) = 0\) ,直接 \(\mathcal O(n)\) 递推即可
关于下降幂。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 1e9+7;
int f[maxn],fac[maxn],inv[maxn],n,p[maxn];
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(yui);
#endif
io >> n;
fac[0] = inv[0] = 1; int pow2 = ksm(2,n);
try(i,1,n) fac[i] = fac[i-1] * i % mod;
p[1] = pow2 - 1;
try(i,2,n) p[i] = p[i-1] * (pow2 - i) % mod;
inv[n] = ksm(fac[n],mod-2);
throw(i,n-1,1) inv[i] = inv[i+1] * (i + 1) % mod;
// jb(p(2));
// return 0;
try(i,3,n) f[i] = (p[i-1] - f[i-1] - (i - 1) * (pow2 - i + 1) % mod * f[i-2] % mod + mod) % mod;
cout<<(p[n] - f[n] + mod) % mod <<endl;
return 0;
}
}
signed main() {return xin::main();}
七负我
这个有一个结论。
就是这个题目我们要找到图中的最大团。
然后平均分配就是答案。
忘了怎么证明的了。
然后状态压缩一下,之后 \(meet\;in\;middle\) 就好了
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
inline void out(int x)
{
cout<<"x = ";
int st = 0;
throw(i,30,1) if(x & (1 << i - 1)) {st = i; break;}
throw(i,st,1) cout<<(bool)(x & (1 << i - 1));
cout<<endl;
}
int f[maxn];
int n,m,tim;
int a[maxn],cnt[maxn];
auto get = [](int x)
{
int ret = 0;
while(x) ret += x & 1,x >>= 1;
return ret;
};
inline short main()
{
#ifdef ONLINE_JUDGE
file(nanami);
#endif
io >> n >> m >> tim;
a[1] = 1; try(i,2,40) a[i] = a[i-1] << 1;//,out(a[i]);
int mid = (n + 1) >> 1;
try(i,1,m)
{
register int x,y; io >> x >> y;
a[x] |= 1ll << y - 1; a[y] |= 1ll << x - 1;
}
// try(i,1,3) out(a[i]);
try(s,1,(1 << mid)) cnt[s] = get(s);
try(s,1,(1 << mid) - 1)
{
// int num = 0;
int temp = -1;
try(i,1,mid) if(s & (1 << i - 1)) f[s] = std::max(f[s],f[s xor (1 << i - 1)]),temp &= a[i];
// out(temp);
// jb(cnt[s]);
if((s & temp) == s) f[s] = cnt[s];
// sb(s); jb(f[s]);
}
int ms = n - mid,ans = 0;
try(s,1,(1 << ms) - 1)
{
int temp = -1;
try(i,1,ms) if(s & (1 << i - 1)) temp &= a[i+mid];
// jb(cnt[s]);
// jb(temp);
if((temp & (s << mid)) == (s << mid))
ans = std::max(ans,cnt[s] + f[temp & (1ll << mid) - 1]);//,jb(temp),jb(mid);
}
// jb(ans);
printf("%.6lf\n",(1.0 * tim / ans) * (1.0 * tim / ans) * (ans * (ans - 1)) * 0.5);
return 0;
}
}
signed main() {return xin::main();}
所以这个题目的难处就是在求这个图的最大团上面了。
然而似乎这个问题是 \(NP\) 完全的。
Day -20
;
话说我为啥老是丢掉之前写的博客啊。。
还是模拟赛日。
然后拿了倒数
特殊字符串
首先上来的 \(dp\) 一看没有小样例,直接就跳过了。
然后发现。。
一共。。。
\(68\) 人写过。。
然后我。。
为啥没有去再看看呢
其实就是直接 \(dp\)。
我们设 \(f_{i,j}\) 表示前 \(i\) 个串结尾强制为 \(j\) 这个字母的映射的答案。
那么直接就是
\(a_{k,j}\) 就是输入的贡献。
然后呢。。。
没了。。
我也没了。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
#define int long long
namespace xin
{
int a[27][27];
int f[maxn][27];
int n;
char s[maxn];
char s1[maxn],s2[maxn];
inline short main()
{
freopen("shiki.in","r",stdin); freopen("shiki.out","w",stdout);
io >> n;
scanf("%s",s+1);
int m; io >> m;
try(i,1,m)
{
int k;
scanf("%s%s",s1+1,s2+1); io >> k;
a[s1[1] - 'a' + 1][s2[1] - 'a' + 1] += k;
}
int ans = 0;
memset(f,128,sizeof(f));
// jb(f[1][1]);
try(i,1,n)
{
int j = s[i] - 'a' + 1;
f[i][j] = 0;
try(k,1,26)
f[i][j] = std::max(f[i][j],f[i-1][k] + a[k][j]),f[i][k] = std::max(f[i][k],f[i-1][k]);
ans = std::max(f[i][j],ans);
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
宝可梦
别问我为啥对 pokemon
有一种特殊的情结。
所以我上来就做这个题了。
刚上来发现这个题目是一个大模拟,然后认为需要投入很长的时间。
然后疯狂开始码,然后半个小时写完了。
然后发现这个有一个 \(n=2\) 的部分分数,然后想到了似乎这个只有一条路径。
但是为啥我没有推广一下子呢,其实所有的图都是只有一条路径的啊。
暴力的想法其实就是每次给出的两个坐标都模拟一遍。
但是这样只有垃圾的 \(40pts\)。
因为每一个空闲点一定会被走过至少一次,那么我们可以任意找到一个空白点,之后试探四个方向哪个可以开始走,这样我们就找到了其中的一个点了。
从这个点开始,我们一定可以走完这个图中唯一的道路,这样我们记录一下坐标和它的方向,然后记录走到这个位置的相对时间。
对于每一次询问,做一下差就行了,只不过要注意一下方向等细节。
然后我获得了次垃解
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gec() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<22,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<22],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
// #define int long long
namespace xin
{
int n,m;
std::vector<int> a[maxn/2];
char s[maxn];
class xin_data
{
public:
int x,y;
xin_data(){}
xin_data(int x,int y):x(x),y(y){}
};
auto you = [](const xin_data &x,int type)
{
if(type == 1) return xin_data(x.x,x.y+1);
else if(type == 2) return xin_data(x.x,x.y-1);
else if(type == 3) return xin_data(x.x-1,x.y);
else return xin_data(x.x+1,x.y);
};
auto qian = [](const xin_data &x,int type)
{
if(type == 1) return xin_data(x.x-1,x.y);
else if(type == 2) return xin_data(x.x+1,x.y);
else if(type == 3) return xin_data(x.x,x.y-1);
else return xin_data(x.x,x.y+1);
};
inline int get(char ch)
{
if(ch == 'U') return 1;
else if(ch == 'D') return 2;
else if(ch == 'L') return 3;
else return 4;
}
auto zz = [](int type)
{
if(type == 1) return 3;
else if(type == 2) return 4;
else if(type == 3) return 2;
else return 1;
};
auto yz = [](int type)
{
if(type == 1) return 4;
else if(type == 2) return 3;
else if(type == 3) return 1;
else return 2;
};
class xin_map
{
private:
friend bool operator < (const xin_map &x,const xin_map &y)
{
if(x.x xor y.x) return x.x < y.x;
if(x.y xor y.y) return x.y < y.y;
return x.nt < y.nt;
}
public:
int x,y,nt;
xin_map(){}
xin_map(int x,int y,int nt):x(x),y(y),nt(nt){}
};
std::map<xin_map,int>vis;
auto query = [](xin_data now,xin_data goal,char type)
{
int ret = 0,nt = get(type),gt = nt;
xin_data pre = xin_data(0,0);
now = qian(now,nt); ret ++;
vis[xin_map(now.x,now.y,nt)] = ret;
while(now.x != goal.x or now.y != goal.y or nt != gt)
{
// vis[xin_map(now.x,now.y,nt)] = ret;
// sb(now.x); sb(now.y); jb(ret);
if(!a[you(now,nt).x][you(now,nt).y])
{
while(!a[qian(now,nt).x][qian(now,nt).y])
{
if(now.x == goal.x and now.y == goal.y and nt == gt) return ret;
nt = zz(nt);
if(now.x == goal.x and now.y == goal.y and nt == gt) return ret;
}
now = qian(now,nt); ret ++;
vis[xin_map(now.x,now.y,nt)] = ret;
}
else
{
now = you(now,nt); ret ++;
nt = yz(nt);
vis[xin_map(now.x,now.y,nt)] = ret;
}
}
return ret;
};
inline char get_alpha(int x)
{
if(x == 1) return 'U';
else if(x == 2) return 'D';
else if(x == 3) return 'L';
else return 'R';
}
inline void init(int &tot)
{
try(i,1,n) try(j,1,m)
{
try(t,1,4)
{
if(a[i][j] and a[qian(xin_data(i,j),t).x][qian(xin_data(i,j),t).y])
{
// debug;
tot = query(xin_data(i,j),xin_data(i,j),get_alpha(t));
// ok = 1;
return;
}
}
}
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(pokemon);
#endif
scanf("%d%d",&n,&m);
try(i,1,m) a[0].emplace_back(0),a[n+1].emplace_back(0);
try(i,1,n)
{
a[i].emplace_back(0);
scanf("%s",s+1);
try(j,1,m) a[i].emplace_back((s[j] == '.'));//,cout<<a[i][j];
a[i].emplace_back(0);
a[i].reserve(m + 10);
// cout<<endl;
}
int qnum; scanf("%d",&qnum);
int tot; init(tot);
// jb(query(xin_data(1,1),xin_data(1,1),'R'));
// jb(tot);
// try(i,1,n) {try(j,1,m) cout<<std::setw(4)<<vis[xin_map(i,j,1)]; cout<<endl;}cout<<endl;
// try(i,1,n) {try(j,1,m) cout<<std::setw(4)<<vis[xin_map(i,j,2)]; cout<<endl;}cout<<endl;
// try(i,1,n) {try(j,1,m) cout<<std::setw(4)<<vis[xin_map(i,j,3)]; cout<<endl;}cout<<endl;
// try(i,1,n) {try(j,1,m) cout<<std::setw(4)<<vis[xin_map(i,j,4)]; cout<<endl;}cout<<endl;
try(cse,1,qnum)
{
register int x,y,goalx,goaly;
scanf("%d%d%d%d%s",&x,&y,&goalx,&goaly,s+1);
if(x == goalx and y == goaly) {puts("0"); continue;}
int ans = inf;
try(i,1,4)
if(vis[xin_map(goalx,goaly,i)])
{
int temp = (vis[xin_map(goalx,goaly,i)] - vis[xin_map(qian(xin_data(x,y),get(s[1])).x,qian(xin_data(x,y),get(s[1])).y,get(s[1]))]);
// jb(temp);
if(temp < 0) temp += tot;
ans = std::min(ans,temp + 1);
}
if(ans < 0) ans += tot;
printf("%d\n",ans);
// cout<<ans<<endl;
// sb(vis[xin_map(goalx,goaly,1)]); jb(vis[xin_map(x,y,get(s[1]))]);
// printf("%d\n",vis[xin_map(goalx,goaly,4)] - vis[xin_map(x,y,get(s[1]))]);
// printf("%d\n",query(xin_data(x,y),xin_data(goalx,goaly),s[1]));
}
return 0;
}
}
signed main() {return xin::main();}
矩阵
直接搜索就行,因为大概率搜不下去。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<22,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
char buf[1<<22],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
// #define int long long
namespace xin
{
const int dx[] = {0,0,-1,1},dy[] = {1,-1,0,0};
int *a[40002];
int n,m;
bool sp = 1;
class xin_data
{
public:
int x,y,bei,tim;
xin_data(){}
xin_data(int x,int y,int bei = 0,int tim = 0):x(x),y(y),bei(bei),tim(tim){}
}q[maxn];
int zhi = 0,ans,cnt;
void bfs(const xin_data &now)
{
q[zhi = 1] = now;
int ms = 1e7;
while(zhi)
{
xin_data x = q[zhi--];
if(cnt >= ms) {cout<<(1<<4)<<endl; exit(0);}
try(i,0,3)
{
xin_data p = xin_data(x.x + dx[i],x.y + dy[i]);
if(p.x >= 1 and p.x <= n and p.y >= 1 and p.y <= m)
{
++cnt;
if(a[p.x][p.y] % a[x.x][x.y] == 0 and (!x.bei or (x.bei and a[p.x][p.y] / a[x.x][x.y] == x.bei)))
{
q[++zhi] = xin_data(p.x,p.y,a[p.x][p.y] / a[x.x][x.y],x.tim + 1);
// jb(x.tim + 2);
ans = std::max(ans,x.tim + 2);
}
}
}
}
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(matrix);
#endif
io >> n >> m;
if(n == 1 and m == 1) return puts("1"),0;
// if(n >= 5000) return cout<<10<<endl,0;
// try(i,0,m+1) a[0].emplace_back(0),a[n+1].emplace_back(0);
a[0] = new int [m + 2]; a[n+1] = new int [m + 2];
try(i,1,n)
{
a[i] = new int [m + 2];
try(j,1,m)
{
int x; io >> x; a[i][j] = x;
// if(j - 1 and a[i][j] != a[i][j-1]) sp = 0;
if((i - 1 and a[i][j] == a[i-1][j]) or (j - 1 and a[i][j] == a[i][j-1]))
return puts("-1"),0;
}
// a[i].emplace_back(0);
// a[i].reserve(m + 10);
}
// if(sp) {puts("-1"); return 0;}
try(i,1,n) try(j,1,m) bfs(xin_data(i,j));
jb(cnt);
cout<<std::max(ans,1)<<endl;
return 0;
}
}
signed main() {return xin::main();}
乘法
别问,问就是做不出来。
但是这个题目的分段打表属实涨知识了。
可以拿到 \(30pts\) 的高分。
但是为啥我的暴力没有一点分呢。。。
下午5:10
教练:一会儿可以出去玩儿会儿。
然后开始乒乓球颓废生活。
fengwu:我要打爆七百信。
然后。。。
fengwu
自闭。
晚上8:00
太虚:出分了。
机房哗然,然后开始查分。
发现沈队一旁镇定地查分,丝毫不慌。
再怎么挂也是HE rk1
然后土豆服务器被挤爆了,几乎没有几个人能够上去。
之后摇摆b首先查到分数,然后他第一个题目的 \(40pts\) 被 \(CCF\) 的数据抬到了 \(60pts\)。
表示羡慕。
之后终于看到分数,发现自己四处爆零的廊桥分配竟然有 \(55pts\),突然开始感谢 \(CCF.jpg\)
四处爆零
话说沈队真的是 \(360pts\)
Day -19
今天并没有模拟赛。
昨天看的垃圾 \(dp\) 守望者的逃离今天决定扔掉。
因为没有一个题解写的 \(dp\)
做了一个教主的花园
教主的花园
以为是区间 \(dp\),但是似乎并不能用区间 \(dp\) 转移。
首先如果不考虑这个是一个环的话,那么我们直接可以从 \(1\) 推到 \(n\)。
那这样该怎么列 \(dp\) 方程呢。
我们设 \(f_{i,0/1/2,0/1}\) 分别表示前 \(i\) 个,第 \(i\) 个选择了哪种树苗,之后是否比旁边的高的答案。
那么这样就分类转移。
- \(f_{i,0,0} = \max(f_{i-1,1,1},f{i-1,2,1})+a_{i,0}\)
- \(f_{i,1,0} = f_{i-1,2,1}+a_{i,1}\)
- \(f_{i,1,1} = f_{i-1,0,0}+a_{i,1}\)
- \(f_{i,2,1} = \max(f_{i-1,0,0},f_{i-1,1,0})+a_{i,0}\)
这个的意义还是比较明显的。
剩下的就是我们要考虑这个东西是一个环的情况了。
如果这个是一个环,那么我们还要考虑 \(1\) 和 \(n\) 的关系。
这个因为其实只有 \(3\) 种状态,枚举一下 \(1\) 选择的树苗,然后分别做一次 \(dp\) 就好了。
最后选择合法的方案统计答案。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
#define int long long
namespace xin
{
int f[maxn][3][3];
int a[maxn][3];
int n,ans;
inline short main()
{
io >> n;
try(i,1,n)
io >> a[i][0] >> a[i][1] >> a[i][2];
try(j,0,2)
{
memset(f[1],0,sizeof(f[1]));
f[1][j][1] = f[1][j][0] = a[1][j];
try(i,2,n)
{
f[i][0][0] = max(f[i-1][1][1],f[i-1][2][1]) + a[i][0];
f[i][1][0] = f[i-1][2][1] + a[i][1];
f[i][1][1] = f[i-1][0][0] + a[i][1];
f[i][2][1] = max(f[i-1][0][0],f[i-1][1][0]) + a[i][2];
}
try(i,0,j-1) ans = max(ans,f[n][i][0]);
try(i,j+1,2) ans = max(ans,f[n][i][1]);
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
P1353 [USACO08JAN]Running S
又是一个 \(dp\)
上来状态其实应该还是比较好想的。
\(f_{i,j}\) 即为 \(i\) 分钟时,\(j\) 疲劳度时的答案。
那么我们考虑如何转移
然后自然 \(f_{n,0}\) 为答案
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
#define int long long
namespace xin
{
int f[10001][501];
int a[maxn];
int n,m;
inline short main()
{
io >> n >> m;
try(i,1,n) io >> a[i];
// memset(f,128,sizeof(f));
// f[1][0] = 0; f[1][1] = a[1];
try(i,1,n)
{
// try(j,0,m) f[i][j] = f[i-1][j+1];
try(j,1,std::min(i,m)) f[i][0] = std::max(f[i][0],std::max(f[i-1][0],f[i-j][j]));
try(j,0,m)
{
if(j - 1 >= 0) f[i][j] = std::max(f[i][j],f[i-1][j-1] + a[i]);
// if(j + 1 <= m) f[i][j] = std::max(f[i][j],f[i-1][j+1]);
}
}
// try(i,1,n)
// {
// try(j,0,m)
// cout<<f[i][j]<<' ';
// cout<<endl;
// }
cout<<f[n][0]<<endl;
return 0;
}
}
signed main() {return xin::main();}
扩展域并查集
高级玩意,感觉像是一些思维游戏里面出的题目。
至于为啥要学这个玩意呢,似乎是因为这一类的题目似乎完全连暴力都无法打出。
所以就只能学这个东西了。
其实本来的并查集就是从 \(1\) 到 \(n\)
也就是
try(i,1,n) fa[i] = i;
但是这个扩展域会变成两倍的东西,是因为他要分开两个域。
一般来说同样一个东西赋予了他两个性质,将其本身只有一个的节点分裂成为两个,之后直接维护节点之间的连通性从而得到某些节点之间的关系。
try(i,1,(n<<1)) fa[i] = i;
所以也就变成了这个样子。
poj1733/acwing239 party game
这个题目要做一个转化。
如果奇偶性是相同的,那么 \(he_{r}\) 与 \(he_{l-1}\) 的奇偶性相同。
如果不相同,则不相同。
其中 \(he\) 就代表着前缀和。
那么我们维护的东西就像是下图。
其中 \(0\),\(3\) 维护的是其中一个节点的奇数域与偶数域。
当询问是偶数的时,那么代表 \((l-1)_{odd}\) 与 \(r_{odd}\),\((l-1)_{even}\) 与 \(r_{even}\) 可以互相推出。
如果询问时奇数个,那么代表 \((l-1)_{odd}\) 与 \(r_{even}\) 与 \(r_{odd}\),\((l-1)_{even}\) 可以互相推出。
那么只要每一次先检验一下,之后再合并就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 4e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
#define int long long
namespace xin
{
class xin_data
{
public:
int l,r,val;
}d[maxn];
int lisan[maxn],cnt = 0;
int n,m;
char s[maxn];
int fa[maxn];
inline int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
auto merge = [](int x,int y)
{
x = find(x); y = find(y);
if(x == y) return;
fa[x] = y;
};
inline short main()
{
freopen("t.txt","r",stdin);
io >> n >> m;
try(i,1,m)
{
io >> d[i].l >> d[i].r;
scanf("%s",s+1);
d[i].val = s[1] == 'o';
lisan[++cnt] = d[i].l - 1; lisan[++cnt] = d[i].r;
}
std::sort(lisan+1,lisan+cnt+1); int k = std::unique(lisan + 1,lisan + cnt + 1) - (lisan + 1);
try(i,1,m)
d[i].l = std::lower_bound(lisan+1,lisan+k+1,d[i].l-1) - lisan,d[i].r = std::lower_bound(lisan+1,lisan+k+1,d[i].r) - lisan;
// sb(d[i].l),jb(d[i].r);
// debug;
try(i,1,(m << 2)) fa[i] = i;
try(i,1,m)
{
int oddx = d[i].l,evenx = d[i].l + k,oddy = d[i].r,eveny = d[i].r + k;
if(!d[i].val)
{
if(find(oddx) == find(eveny))
{
cout<<i - 1<<endl;
return 0;
}
merge(oddx,oddy); merge(evenx,eveny);
}
else
{
if(find(oddx) == find(oddy))
{
cout<< i - 1<<endl;
return 0;
}
merge(oddx,eveny); merge(oddy,evenx);
}
}
cout<<m<<endl;
return 0;
}
}
signed main() {return xin::main();}
P4865 Samcompu Loves Water
这个题目看错题目了。。。
还是因为题目太长了。。。
不不不,是因为题目太水了
我们到最后发现 \(k\leq1000\),所以复杂度一定和 \(k\) 有关。
我们可以将询问离线。
那么我们再看剩下的所有边,找到比它小的边,然后每一个合法的边的两个点并查集起来,记得记录下来 \(siz\) 的大小。
之后每次合并一次之前,我们就先 ans+=siz[fx]*siz[fy]*2
,乘二是因为这个东西的 \((x,y)\) 和 \((y,x)\) 是不同的方案。
之后将每一个合并就行了,注意离线的统计答案。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 4e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
#define int long long
namespace xin
{
class xin_data
{
public:
int tim,k,id;
}d[maxn];
class xin_edge
{
public:
int x,y,w,id;
}edge[maxn];
int t,n;
std::vector<xin_data>vec[10001];
int fa[maxn],siz[maxn];
inline int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
int ans[maxn];
inline short main()
{
io >> t >> n;
try(i,1,n-1) io >> edge[i].x >> edge[i].y >> edge[i].w,edge[i].id = i;
try(i,1,t)
{
io >> d[i].tim >> d[i].k; d[i].id = i;
vec[d[i].k].emplace_back(d[i]);
}
std::sort(edge+1,edge+n,[](const xin_edge &x,const xin_edge &y) {return x.w < y.w;});
try(i,1,n-1) if(vec[i].size())
{
std::sort(vec[i].begin(),vec[i].end(),[](const xin_data &x,const xin_data &y){return x.tim < y.tim;});
try(j,1,n) fa[j] = j,siz[j] = 1;
int zhi = 1,temp = 0;
for(auto v : vec[i])
{
try(j,zhi,n-1)
{
if(i == edge[j].id) continue;
if(edge[j].w >= v.tim) break;
zhi = j + 1;
int x = edge[j].x,y = edge[j].y;
int fx = find(x),fy = find(y);
temp += siz[fx] * siz[fy] * 2;
fa[fy] = fx;
siz[fx] += siz[fy]; siz[fy] = 0;
}
// try(j,1,n-1) if(edge[j].id == i) {ans[v.id] = siz[find(edge[i].x)] * siz[find(edge[i].y)] * 2; break;}
ans[v.id] = temp;
}
}
try(i,1,t) printf("%lld\n",ans[i]);
return 0;
}
}
signed main() {return xin::main();}
CF1594D The Number of Imposter
今天似乎做了一堆并查集的题目。
这个题目还是刚刚学的扩展域并查集
实现方法似乎比例题还简单一些。
我们还是使用这个图说事
这次每个点的两个性质就是真话和假话。
之后我们维护每一个联通块的 \(siz\)
因为我们并不能确定哪一个是真话的,哪一个是假话的。
换句话说,就是我们可以任意选择一种成为假话的或者是真话的。
因为题目当中要求的是最大的,所以对于每一个联通快取 \(\max\) 就行了。
不合法的情况就是在 \(find(i) == find(i+n)\) 的时候,因为 \(i+n\) 和 \(i\) 其实是不同的性质,但是它强行搞成一个了,所以直接输出 \(-1\)。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 4e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
#define int long long
namespace xin
{
int fa[maxn];
int n,T,m,siz[maxn];
char s[maxn];
inline int find(int x)
{
if(fa[x] == x) return fa[x];
// siz[fa[x]] += siz[x]; siz[x] = 0;
return fa[x] = find(fa[x]);
}
inline void merge(int x,int y)
{
x = find(x); y = find(y);
if(x == y) return ;
siz[x] += siz[y]; siz[y] = 0;
fa[y] = x;
}
inline short main()
{
io >> T;
while(T--)
{
io >> n >> m;
try(i,1,(n << 1)) fa[i] = i,siz[i] = (i <= n);
try(i,1,m)
{
register int x,y; io >> x >> y; scanf("%s",s+1);
// printf("%s\n",s+1);
if(s[1] == 'i')
{
merge(x,y+n); merge(x+n,y);
}
else
{
merge(x,y); merge(x+n,y+n);
}
}
int ans = 0;
try(i,1,n)
{
if(find(i) == find(i + n)) {ans = -1; break;}
ans += std::max(siz[find(i)],siz[find(i+n)]);
siz[find(i)] = siz[find(i+n)] = 0;
}
printf("%lld\n",ans);
}
return 0;
}
}
signed main() {return xin::main();}
Day -18
老实交代,其实这一天的博客咕掉了。
然而今天来补上。
集合均值
感觉自己看到期望的题目就害怕。
但是其实这个题目远远不如之前的题目难。
说白了,就是一个签到的题目。
然而因为自己看到是期望的题目所以就跳过了。。。。
这个题目考察的最多最多就是一个期望的线性性,还有就是卡常
话说对于常数的要求似乎是很大的。
因为一直开着 #define int long long
所以卡了老半天。
线性求逆元行了。
话说都 1e7 了,还能怎么求
这个题目的解法就是我们发现对于每一个数的贡献其实都是相同的,那么我们只要观察它的概率就好了。
我们容易发现,这个数字的概率其实是一个后缀和的关系。
大概是这个样子:
我们对于每一列分别统计,那么每一列的贡献就是 \(\frac{i}{i+1}\)
那么问题就很好解决了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
// #define int long long
namespace xin
{
const int mod(998244353);
int n,m;
ll inv[maxn*20],he,ans;
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(mos);
#endif
io >> n >> m;
// he %= mod;
inv[0] = inv[1] = 1;
// for(int i=2;i<=n*m+1;++i) inv[i]=mod-mod/i*inv[mod%i]%mod;
int ms = n*m+1;
try(i,2,ms)
{
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
// ans += (i-1) * inv[i] % mod; ans -= (ans >= mod) ? mod : 0;
}
// return 0;
// return 0;
// try(i,1,ms-1) (ans += i * inv[i+1] % mod) %= mod;
ll temp = 0;
try(i,1,ms-1) (temp += inv[i+1] * i) %= mod;
temp = temp * inv[n*m] % mod;
// jb(temp);
// ans *= he * inv[n] % mod;
he = 0;
try(i,1,n)
{
register ll x; io >> x;
(he += x * m) %= mod;
}
// jb(he);
cout<<temp * he % mod<<endl;
return 0;
}
}
signed main() {return xin::main();}
聚烷撑乙二醇
其实也是一个简单题。
然后因为概率,我又跳过了
我们从后面向前推,假设从 \(i\) ~ \(n\) 的概率是 \(f_i\)。
那么有三种情况。
- \(f_{i+1}\ge r_i\) 那么这个 \(f_i=f_{i+1}\)
- \(f_{i+1}\leq l_i\) 那么这个 \(f_i=(l_i+r_i)/2\)
- 这种情况是比较麻烦的,其实也不麻烦,就是我们统计左边和右边的概率乘到上面就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
auto max = [](auto x,auto y) {return x >y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
// #define int long long
namespace xin
{
using ld = long double;
const ld eps = 1e-30;
int n;
ld f[maxn],l[maxn],r[maxn],ans;
inline short main()
{
#ifdef ONLINE_JUDGE
file(pag);
#endif
scanf("%d",&n);
bool sp = 1;
try(i,1,n)
{
scanf("%Lf%Lf",&l[i],&r[i]);
if(l[i] != r[i]) sp = 0;
ans = std::max(ans,std::max(l[i],r[i]));
}
if(sp) return printf("%.5Lf\n",ans),0;
f[n] = (l[n] + r[n]) / 2.0;
throw(i,n-1,1)
{
register ld x = f[i + 1];
// jb(x);
if(x > r[i]) f[i] = x;
else if(x < l[i]) f[i] = (r[i] + l[i]) * 0.5;
else f[i] = (x - l[i]) / (r[i] - l[i] + eps) * x + ((r[i] - x) / (r[i] - l[i] + eps)) * (r[i] + x) * 0.5;
// sb(f[i])
}
printf("%.5Lf\n",f[1]);
return 0;
}
}
signed main() {return xin::main();}
技术情报局
暴力。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
namespace GenHelper {
unsigned z1, z2, z3, z4, b;
unsigned rand_() {
b = ((z1 << 6) ^ z1) >> 13;
z1 = ((z1 & 4294967294U) << 18) ^ b;
b = ((z2 << 2) ^ z2) >> 27;
z2 = ((z2 & 4294967288U) << 2) ^ b;
b = ((z3 << 13) ^ z3) >> 21;
z3 = ((z3 & 4294967280U) << 7) ^ b;
b = ((z4 << 3) ^ z4) >> 12;
z4 = ((z4 & 4294967168U) << 13) ^ b;
return (z1 ^ z2 ^ z3 ^ z4);
}
} // namespace GenHelper
std::vector<ll> a;
void get (int n, unsigned s, int l, int r) {
using namespace GenHelper;
a.emplace_back(0);
z1 = s;
z2 = unsigned((~s) ^ 0x233333333U);
z3 = unsigned(s ^ 0x1234598766U);
z4 = (~s) + 51;
for (int i = 1; i <= n; i ++) {
int x = rand_() & 32767;
int y = rand_() & 32767;
a.push_back(l + (x * 32768 + y) % (r - l + 1));
}
// return a;
}
#define int long long
namespace xin
{
int st[maxn],top,debt[maxn],he[maxn],ret[maxn];
int n,s,l,r,mod,ans;
inline short main()
{
#ifdef ONLINE_JUDGE
file(tio);
#endif
io >> n >> s >> l >> r >> mod;
get(n,s,l,r);
// for(auto v : a) cout<<v<<' '; cout<<endl;
try(i,1,n)
{
debt[top] = debt[top] * (a[i] %= mod) % mod;
int now = a[i];
while(top and a[st[top]] <= a[i])
{
(now += debt[top] * he[top] % mod) %= mod;
(debt[top-1] *= debt[top]) %= mod; top --;
}
(ret[top + 1] = a[i] * now % mod + ret[top] * debt[top] % mod) %= mod;
st[++top] = i; debt[top] = 1; he[top] = now;
ans += ret[top]; ans %= mod;
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
肯德基
莫比乌斯。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x];i;i=edge[i].next)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) cout<<#x" = "<<x<<' '
#define jb(x) cout<<#x" = "<<x<<endl
#define debug cout<<"debug"<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
char buf[1<<20],*p1 = buf,*p2 = buf; int ak; typedef long long ll; typedef unsigned long long ull;
class xin_stream{public:template<typename type>inline xin_stream &operator >> (type &s)
{
register int f = 0;s = 0; register char ch = gc();
while(!isdigit(ch)) {f |= ch == '-'; ch = gc();}
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+7; const ll llinf = 1e18+7;
#define int unsigned long long
namespace xin
{
int T;
signed mu[maxn];
signed tot,p[maxn];
bool vis[maxn];
int s[maxn];
inline void pre_work(int ms)
{
mu[1] = vis[1] = 1; s[1] = 1;
try(i,2,ms)
{
if(!vis[i]) mu[i] = -1,p[++tot] = i;
for(register int j=1;j<=tot and i * p[j] <= ms;++j)
{
register int x = i * p[j];
vis[x] = 1;
if(i % p[j] == 0) {mu[x] = 0; break;}
else mu[x] = -mu[i];
}
// jb(mu[i]);
s[i] = s[i-1] + mu[i] * i * i;
// cout<<mu[i]<<endl;
// if(i == (int)1e7) cout<<s[i]<<endl;
// if(s[i] < s[i-1]) debug;
// if(i <= 10) sb(mu[i]),sb(i),jb(s[i]);
}
}
inline int sig(int n) {return !(n & 1) ? (n / 2 * (n + 1)) : ((n + 1) / 2 * n);}
int n;
inline void work()
{
// try(i,1,10) cout<<s[i]<<endl;
int ms = std::sqrt(n); ull ans = 0;
for(int l=1,r;l<=ms;l=r+1)
{
r = std::sqrt(n / (n / l / l));
ans += (s[r] - s[l-1]) * sig(n / l / l);
// jb(s[r] - s[l-1]);
}
cout<<ans<<endl;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(kfc);
#endif
int T; io >> T;
pre_work(1e7);
while(T--)
{
io >> n;
work();
}
return 0;
}
}
signed main() {return xin::main();}
Day -17
吸取前面场次的经验。
\(\huge{\text{不能跳题!!!}}\)
然后开始干 \(T1\)
然后发现这个 \(T1\) 有一点小难。
然后干了 \(1h\),发现这个东西实在是有点难。
但是自己推出来了一个 \(\mathcal O(n2^{log2(t)})\) 的 \(dp\)
发现理论可以 \(40pts\),但是认为似乎这个题目会人均签到,但是自己只写了这一点分数,有点失落。
\(T2\) 发现是一个期望。
不能跳!!!
然后发现有很多的部分分数。
整整似乎有 \(70pts\)
然后最后得了 \(50pts\)
之后改成了 \(70pts\) 的,只不过就是打了 \(3\) 个 \(dfs\) 和 \(2\) 个\(bfs\)。
淦。
70pts
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18;
#define int long long
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],cnt_b;
inline void add(int x,int y) {edge[++cnt_b].ver = y; edge[cnt_b].next = head[x]; head[x] = cnt_b;}
int n,m,k;
const int mod = 998244353;
int key[maxn],fa[maxn],dep[maxn],siz[maxn],hson[maxn],top[maxn];
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
void dfs1(int x,int f)
{
dep[x] = dep[f] + 1; siz[x] = 1; fa[x] = f;
go(i,x) if(y != f)
{
dfs1(y,x);
siz[x] += siz[y];
if(siz[y] > siz[hson[x]]) hson[x] = y;
}
}
void dfs2(int x,int t)
{
top[x] = t;
if(hson[x]) dfs2(hson[x],t);
go(i,x)
{
if(y == fa[x] or y == hson[x]) continue;
dfs2(y,y);
}
}
inline int lca(int x,int y)
{
while(top[x] xor top[y])
{
if(dep[top[x]] < dep[top[y]]) std::swap(x,y);
x = fa[top[x]];
}
return dep[x] > dep[y] ? y : x;
}
inline int dis(int x,int y) {return dep[x] + dep[y] - 2 * dep[lca(x,y)];}
int a[maxn],zhi,temp[maxn];
int ans = 0,cnt = 0;
void dfs()
{
if(zhi == k)
{
try(i,1,zhi) temp[i] = a[i];
std::sort(temp+1,temp+zhi+1);
// try(i,1,zhi) cout<<key[temp[i]]<<' '; cout<<endl;
int ret = inf;
do
{
int he = 0;
try(i,1,zhi-1) he += dis(key[temp[i]],key[temp[i+1]]);
// jb(he);
// if(he == 7) {try(i,1,zhi) cout<<key[temp[i]]<<' '; cout<<endl;}
ret = std::min(ret,he);
} while (std::next_permutation(temp+1,temp+zhi+1));
// sb(key[temp[1]]); sb(key[temp[2]]); jb(ret);
(ans += ret) %= mod;
// debug;
++cnt;
return ;
}
try(i,a[zhi]+1,m)
{
a[++zhi] = i;
dfs();
zhi --;
}
}
int ton[maxn];
bool vis[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(tree);
#endif
io >> n >> m >> k;
try(i,1,m) io >> key[i];
try(i,1,n-1)
{
register int x,y; io >> x >> y;
add(x,y); add(y,x);
}
dfs1(1,0); dfs2(1,1);
if(k == 2)
{
try(i,1,m) try(j,i+1,m)
{
++cnt;
(ans += dis(key[i],key[j])) %= mod;
}
// jb(ans); jb(cnt);
cout<<ans * ksm(cnt,mod-2) % mod<<endl;
return 0;
}
else if(k == m and n >= 10)
{
// debug;
int allca = key[1];
try(i,2,m) allca = lca(allca,key[i]);
memset(head,0,sizeof(int) * (n + 1)); cnt_b = 0;
try(i,1,m)
{
int x = key[i];
// jb(x);
// debug;
while(!vis[x] and x != allca)
{
vis[x] = 1;
add(fa[x],x); add(x,fa[x]);
ans ++;
// sb(fa[x]); jb(x);
x = fa[x];
}
}
// jb(ans);
int far1;
memset(vis,0,sizeof(bool) * (n + 1));
std::queue<std::pair<int,int>>q; vis[key[1]] = 1; q.push(std::make_pair(key[1],0));
while(q.size())
{
register int x = q.front().first; q.pop();
far1 = x;
go(i,x) if(!vis[y])
{
vis[y] = 1; q.push(std::make_pair(y,0));
}
}
std::pair<int,int>far2;
memset(vis,0,sizeof(bool) * (n + 1));
q.push(std::make_pair(far1,0)); vis[far1] =1;
while(q.size())
{
std::pair<int,int> x = q.front(); q.pop();
far2 = x;
go(i,x.first) if(!vis[y])
{
vis[y] = 1; q.push(std::make_pair(y,x.second+1));
}
}
cout<<ans * 2 - far2.second<<endl;
// sb(far2.first); jb(far2.second);
return 0;
}
else
{
// debug;
dfs();
// jb(ans); jb(cnt);
cout<<ans * ksm(cnt,mod-2) % mod<<endl;
return 0;
}
return 0;
}
}
signed main() {return xin::main();}
按位或
似乎不是很简单。
这个是一个容斥。
就是关于一个只有 \(0,1,2\) 剩余系的容斥。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18;
#define int long long
namespace xin
{
const int mod = 998244353;
int n,t;
int c[1001][1001];
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(or);
#endif
io >> n >> t;
try(i,0,1000)
{
c[i][0] = 1;
try(j,1,i)
(c[i][j] = c[i-1][j] + c[i-1][j-1]) %= mod;
}
// jb(c[3][1]);
// jb(c[2][1]); jb(c[2][0]);
int ans = 0;
int num1 = 0,num2 = 0;
throw(i,62,0)
{
if((t >> i) & 1) num1 ++;
i --;
}
throw(i,63,0)
{
if((t >> i) & 1) num2 ++;
i --;
}
// sb(num1); jb(num2);
// jb(c[3][2]);
try(x,0,num1) try(y,0,num2)
{
int temp = 0,base = (num1 + num2 - x - y) & 1 ? -1 : 1;
// cout<<base<<endl;
try(i,0,x) try(j,0,y) if(!((i - j) % 3))
(temp += c[x][i] * c[y][j] % mod) %= mod;
// jb(temp);
(ans += ksm(temp,n) * c[num1][x] % mod * c[num2][y] % mod * base % mod + mod) %= mod; ans = (ans + mod) % mod;
// jb(ans);
base = -base;
}
cout<<ans % mod<<endl;
return 0;
}
}
signed main() {return xin::main();}
最短路径
我们先转化一下题意,然后发现这个其实就是求虚树上边长的期望的二倍减去虚树上面最长链的期望
那么我们考虑每一个边的贡献。
之后再 \(m^3\) 枚举最长链就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
// #define gc() getchar()
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18;
#define int long long
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],cnt_b;
inline void add(int x,int y) {edge[++cnt_b].ver = y; edge[cnt_b].next = head[x]; head[x] = cnt_b;}
int n,m,k;
const int mod = 998244353;
int key[maxn],fa[maxn],dep[maxn],siz[maxn],hson[maxn],top[maxn];
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
void dfs1(int x,int f)
{
dep[x] = dep[f] + 1; siz[x] = 1; fa[x] = f;
go(i,x) if(y != f)
{
dfs1(y,x);
siz[x] += siz[y];
if(siz[y] > siz[hson[x]]) hson[x] = y;
}
}
void dfs2(int x,int t)
{
top[x] = t;
if(hson[x]) dfs2(hson[x],t);
go(i,x)
{
if(y == fa[x] or y == hson[x]) continue;
dfs2(y,y);
}
}
inline int lca(int x,int y)
{
while(top[x] xor top[y])
{
if(dep[top[x]] < dep[top[y]]) std::swap(x,y);
x = fa[top[x]];
}
return dep[x] > dep[y] ? y : x;
}
int fac[maxn],inv[maxn];
inline int c(int n,int m) {if(m > n) return 0; return fac[n] * inv[m] % mod * inv[n-m] % mod;}
// inline int dis(int x,int y) {return dep[x] + dep[y] - 2 * dep[lca(x,y)];}
int big[maxn];
bool imp[maxn];
int ans = 0;
void dfs3(int x,int f)
{
big[x] = imp[x];
go(i,x) if(y != f)
{
dfs3(y,x);
big[x] += big[y];
}
(ans += 2ll * (c(m,k) - c(big[x],k) - c(m - big[x],k) + 4ll * mod)) %= mod;
}
int d[maxn];
void dfs(int x,int f)
{
d[x] = d[f] + 1;
go(i,x) if(y != f)
dfs(y,x);
}
int dis[301][301];
inline short main()
{
#ifdef ONLINE_JUDGE
file(tree);
#endif
io >> n >> m >> k;
fac[0] = inv[0] = 1;
try(i,1,m) fac[i] = fac[i-1] * i % mod;
inv[m] = ksm(fac[m],mod-2);
throw(i,m-1,1) inv[i] = inv[i+1] * (i + 1) % mod;
// jb(c(3,1));
try(i,1,m) io >> key[i],imp[key[i]] = 1;
try(i,1,n-1)
{
register int x,y; io >> x >> y;
add(x,y); add(y,x);
}
dfs1(1,0); dfs2(1,1);
try(i,1,m)
{
dfs(key[i],0);
try(j,1,m)
dis[i][j] = d[key[j]] - d[key[i]];//,cout<<dis[i][j]<<endl;
}
dfs3(1,0);
// jb(ans);
try(i,1,m)
{
try(j,i+1,m)
{
int sum=0;
try(l,1,m)
{
if(l==i or l==j)continue;
if(dis[l][i]<dis[i][j] and dis[l][j]<dis[i][j])sum++;
else if(l>i and dis[l][i]<dis[i][j] and dis[l][j]<=dis[i][j])sum++;
else if(l>j and dis[l][i]<=dis[i][j] and dis[l][j]<=dis[i][j])sum++;
}
ans=(ans-c(sum,k-2)*dis[i][j] % mod + mod) % mod;
}
}
cout<<ans * ksm(c(m,k),mod-2) % mod<<endl;
return 0;
}
}
signed main() {return xin::main();}
仙人掌 and 对弈
咕
Day -16
[CSP-S2019] Emiya 家今天的饭 | P5664
话说昨天似乎还留下一个[CSP-S2019] Emiya 家今天的饭 | P5664
然后写了一个垃圾爆搜,获得了 \(32pts\) 高分。。。
32pts
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int mat[101][2001];
int n,m;
int a[maxn],zhi = 0;
int ans = 0;
int b[maxn],cnt = 0;
int num[maxn];
void dfs2(int ms)
{
if(cnt == ms)
{
int he = 1;
memset(num,0,sizeof(int) * (m + 1));
// try(i,1,cnt) cout<<b[i]<<' ';
// cout<<endl;
try(i,1,cnt) num[b[i]] ++;
try(i,1,m) if(num[i] > (ms >> 1)) return ;
try(i,1,cnt)
(he *= mat[a[i]][b[i]]) %= mod;
(ans += he) %= mod;
return;
}
try(i,1,m)
{
b[++cnt] = i;
dfs2(ms);
cnt --;
}
}
void dfs(int ms)
{
if(zhi == ms)
{
// try(i,1,zhi) cout<<a[i]<<' ';
// cout<<endl;
dfs2(ms);
return ;
}
try(i,a[zhi]+1,n)
{
a[++zhi] = i;
dfs(ms);
zhi --;
}
}
inline short main()
{
io >> n >> m;
try(i,1,n) try(j,1,m) io >> mat[i][j];
try(i,2,n) dfs(i);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
之后我们有一个 \(dp\) 的思路,这个可以引导向正解,并且似乎可以得到很多的分数。
我们最最烦的一个限制其实就是那个 \(\frac{k}{2}\) 的那个限制。
我们正难则反一下,那么我们就是说我们其实可以用全部的方案数减去所有不合法的方案数。
那么其实这样也就好办多了。
我们首先考虑全部的方案数,也就是所有合法的和不合法的方案数。
我们设 \(g_{i,j}\) 为前 \(i\) 行,选择了 \(j\) 个的方案数。
那么我们就可以知道总的方案数就是 \(\sum_{i=0}^ng_{n,i}\)
那么这个东西如何转移。
我们记录矩阵的每一行的总和为 \(he_i\)
那么 \(g_{i,j} = g_{i-1,j} + he_i * g_{i-1,j-1}\)
这个东西实际上就表示 上一行选j个的方案数加上上一行选j-1的方案数再乘上这一行的总和
,因为这一行的每一个数都可以选,所以乘上总和。
之后考虑不合法的情况。
我们设 \(f_{i,j,k}\) 为 \(col\) 这一列,前 \(i\) 行,在这一列里面选择了 \(j\) 个,然后在其他列选择了 \(k\) 个的方案数。
那么转移也就是显然:\(f_{i,j,k} = f_{i-1,j-1,k} * a_{i,col} + f_{i-1,j,k-1} * (he_i - a_{i,col})\)
之后做差就是 \(84pts\) 代码。
84pts
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int mat[101][2001];
int n,m;
int he[maxn];
int g[201][401];
int f[41][41][41];
inline short main()
{
io >> n >> m;
try(i,1,n) try(j,1,m) io >> mat[i][j],he[i] += mat[i][j],he[i] %= mod;
g[0][0] = 1;
try(i,1,n) try(j,0,i)
(g[i][j] = g[i-1][j] + (j > 0 ? g[i-1][j-1] * he[i] % mod : 0)) %= mod;
int ans = 0;
try(i,1,n) (ans += g[n][i]) %= mod;
// jb(ans);
try(col,1,m)
{
memset(f,0,sizeof(f));
f[0][0][0] = 1;
try(i,1,n) try(j,0,n) try(k,0,n)
f[i][j][k] = (f[i-1][j][k] + (j > 0 ? f[i-1][j-1][k] * mat[i][col] % mod : 0) + (k > 0 ? f[i-1][j][k-1] * (he[i] - mat[i][col])% mod : 0)) % mod;
try(j,0,n) try(k,0,j-1)
ans = (ans - f[n][j][k] + mod) % mod;
}
// cout<<ans<<endl;
// try(col,1,m)
// {
// memset(f,0,sizeof(f));
// f[0][n] = 1;
// try(i,1,n) try(j,n-i,n+i)
// f[i][j] = (f[i-1][j] + mat[i][col] * f[i-1][j-1] % mod + (he[i] - mat[i][col]) * f[i-1][j+1] % mod) % mod;
// try(i,1,n)
// ans = (ans - f[n][n+i] + mod) % mod;
// }
cout<<ans<<endl;
// cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
之后我们考虑正解的做法。
这个就是我们其实并不关心的是 \(j\) 和 \(k\) 的具体的数值,但是我们关心他们的大小关系。
所以我们把第二维改成 \(f_{i,j}\) 表示 \(col\) 这一列比其他列多了 \(j\) 个。
那么转移类似。
这样就得到了正确的复杂度做法。
100pts
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int mat[101][2001];
int n,m;
int he[maxn];
int g[201][401];
int f[201][401];
inline short main()
{
io >> n >> m;
try(i,1,n) try(j,1,m) io >> mat[i][j],he[i] += mat[i][j],he[i] %= mod;
g[0][0] = 1;
try(i,1,n) try(j,0,i)
(g[i][j] = g[i-1][j] + (j > 0 ? g[i-1][j-1] * he[i] % mod : 0)) %= mod;
int ans = 0;
try(i,1,n) (ans += g[n][i]) %= mod;
// jb(ans);
// try(col,1,m)
// {
// memset(f,0,sizeof(f));
// f[0][0][0] = 1;
// try(i,1,n) try(j,0,n) try(k,0,n)
// f[i][j][k] = (f[i-1][j][k] + (j > 0 ? f[i-1][j-1][k] * mat[i][col] % mod : 0) + (k > 0 ? f[i-1][j][k-1] * (he[i] - mat[i][col])% mod : 0)) % mod;
// try(j,0,n) try(k,0,j-1)
// ans = (ans - f[n][j][k] + mod) % mod;
// }
// cout<<ans<<endl;
try(col,1,m)
{
memset(f,0,sizeof(f));
f[0][n] = 1;
try(i,1,n) try(j,n-i,n+i)
f[i][j] = (f[i-1][j] + mat[i][col] * f[i-1][j-1] % mod + (he[i] - mat[i][col]) * f[i-1][j+1] % mod) % mod;
try(i,1,n)
ans = (ans - f[n][n+i] + mod) % mod;
}
cout<<ans<<endl;
// cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
codeforces 101350 G
一道比较板子的容斥题目。
发现 \(k\) 比较小。
那么就考虑直接枚举 \(k\) 的状态。
我们还是将答案表示为 总共的答案-不合法的答案
那么我们首先就是计算矩阵当中子矩阵的总数量。
发现其实就是拿两条线去卡出矩形就好了。
那么总共的方案数其实就是 \(\dbinom{2}{n+1}*\dbinom{2}{m+1}\)
之后考虑不合法的情况。
我们如果硬算似乎很难算出来,但是我们可以算出来包含 \(i\) 个点的方案数。
那么我们总共的不合法的数量其实就是
\(f_i\) 就代表包含 \(i\) 个点的方案数。
之后用 \(ans\) 减去就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
#define int long long
auto max = [](auto x,auto y) {return x > y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
namespace xin
{
int n,m,k;
int ans = 0;
class xin_data
{
public:
int x,y;
xin_data(){}
xin_data(int x,int y):x(x),y(y){}
}d[maxn];
inline short main()
{
int T; io >> T;
while(T--)
{
io >> n >> m >> k;
ans = ((n + 1) * n / 2) * ((m + 1) * m / 2);
try(i,1,k) io >> d[i].x >> d[i].y;
// cout<<ans<<endl;
int ms = (1 << k) - 1;
int ret = 0;
try(s,1,ms)
{
int cnt = 0;
int temp = s;
while(temp) cnt += (temp & 1),temp >>= 1;
int rx = 0,lx = inf,ry = 0,ly = inf;
try(i,1,k) if((s >> (i - 1)) & 1)
{
rx = max(rx,d[i].x); ry = max(ry,d[i].y);
lx = min(lx,d[i].x); ly = min(ly,d[i].y);
}
// int he = (n - (rx - lx)) * (m - (ry - ly));
int he = lx * ly * (n - rx + 1) * (m - ry + 1);
// sb(lx); sb(rx); sb(ly); jb(ry);
if(cnt & 1) ret += he;
else ret -= he;
}
printf("%lld\n",ans - ret);
}
return 0;
}
}
signed main() {return xin::main();}
[USACO06NOV]Corn Fields G | P1879
这个做法的理论复杂度可能不是很对。
但是不知道为什么我跑得飞快。
\(10\) 个点一共只用了 \(33ms\)
很普通的,我们记录 \(f_{i,s}\) 为 \(i\) 行,当前状态为 \(s\) 的方案数。
那么我们的目标就是 \(\sum f_{n,s}\)
我们考虑这个东西如何转移。
首先就是考虑什么样子的 \(s\) 是合法的。
farmer john
要求我们每一个草地都不能相连,那么我们的 \(s\) 中的每一个有 \(1\) 的二进制位也不能相连。
这个 \(\mathcal O(12)\) 进行一个判断就好了。
然后我们还要求相邻两行的草地也不能有一点相邻。
那么我们分别枚举两行的状态,之后取 &
要求其为 \(0\) 就好了。
总的复杂度应该是 \(\mathcal O(12n2^{2m})\),\(n,m\leq 12\) 似乎不是很对,但是跑得飞快。
注意!!!
&
符号比 ==
的运算优先级还低!!!!!导致我刚开始WA成50了
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
#define int long long
auto max = [](auto x,auto y) {return x > y ? x : y;}; auto min = [](auto x,auto y) {return x < y ? x : y;};
namespace xin
{
const int mod = 100000000;
int f[20][(1 << 13) + 1];
int n,m;
int a[maxn];
inline bool pan(int x)
{
try(i,0,11) if(((x >> i) & 1) and ((x >> i) & 1) == ((x >> i + 1) & 1)) return false;
return true;
}
inline short main()
{
io >> n >> m;
try(i,1,n)
{
try(j,1,m)
{
register int x; io >> x;
if(x) a[i] |= (1 << j - 1);
}
// sb(i); jb(a[i]);
}
f[0][0] = 1;
int ms = (1 << m) - 1;
// try(i,1,ms) if(pan(i)) f[0][i] = 1;
// jb(ms);
// jb(pan(5));
// cout<<(5 & 7)<<endl;
// jb(pan(5));
try(i,1,n)
{
// sb(i); jb(a[i]);
try(s,0,ms) if((s & a[i]) == s and pan(s))
{
// cout<<s<<endl;
try(st,0,ms) if((st & s) == 0 and pan(st))
(f[i][s] += f[i-1][st]) %= mod;
}
}
int ans = 0;
try(s,0,ms) (ans += f[n][s]) %= mod;
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
[NOIP2014 提高组] 寻找道路 | P2296
话说我犯了一年的错误的,今天才查出来啊。
思路这个题目还是非常简单的。
首先我们要找到合法的点,那么反向 \(bfs\) 一遍其实就好了。
之后正向再来一个 \(bfs\) 就能找到最短的了。
但是自己不知道为什么一直 \(40pts\).
最后一直狂调,发现自己的广搜如果手写的队列,一直模拟的其实都是栈,而不是队列。
这样最短的距离就会找错了。。
一道历史遗留问题解决了
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
// #define int long long
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],cnt;
auto add = [](int x,int y) {edge[++cnt].ver = y; edge[cnt].next = head[x]; head[x] = cnt;};
std::pair<int,int>temp[maxn];
int n,m,st,ed,q[maxn],zhi,tim[maxn];
bool vis[maxn];
bool nb[maxn];
int dis[maxn];
std::vector<int>vec[maxn/10];
inline short main()
{
io >> n >> m;
try(i,1,m)
{
register int x,y; io >> x >> y;
add(y,x); vec[x].emplace_back(y);
// temp[i] = std::make_pair(x,y);
}
io >> st >> ed;
// debug;
q[zhi = 1] = ed; vis[ed] = 1;
// debug;
while(zhi)
{
int x = q[zhi--];
go(i,x) if(!vis[y])
{
vis[y] = 1;
q[++zhi] = y;
}
}
if(!vis[st]) {puts("-1"); exit(0);}
memset(head,0,sizeof(int) * (n + 1)); cnt = 0;
// memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge));
// try(i,1,m) add(temp[i].first,temp[i].second);
try(i,1,n)
{
nb[i] = 1;
if(!vis[i]) nb[i] = 0;
if(nb[i]) for(auto y : vec[i]) if(!vis[y])
{
nb[i] = 0; break;
}
}
// try(i,1,n) if(!nb[i]) cout<<"i = "<<i<<endl;
// jb(nb[4575]);
// try(i,1,n) cout<<vis[i]<<endl;
int front = 0,back = 1; dis[st] = 0;
q[++front] = st;
// debug;
while(front >= back)
{
int x = q[back++];
if(!nb[x]) continue;
// sb(zhi); jb(x);
// sb(t); jb(x);
if(x == ed) {printf("%d\n",dis[x]); exit(0);}
for(auto y : vec[x]) if(!dis[y])
{
// jb(y);
dis[y] = dis[x] + 1;
q[++front] = y;
}
}
cout<<-1<<endl;
return 0;
}
}
signed main() {return xin::main();}
[NOIP2010 提高组] 乌龟棋 | P1541
这是一个不是正解的方法。
因为需要卡亿卡常。
我们首先看到 \(50pts\) 做法,发现可以直接5维dp数组
设 \(f_{i,num1,num2,num3,num4}\) 为走到 \(i\) 之后还有 \(num1\) 个 \(1\) 卡片.....
然后容易得到转移:
if(num1 < num[1] and i - 1 >= 1)
f[i][num1][num2][num3][num4] = std::max(f[i][num1][num2][num3][num4],f[i-1][num1+1][num2][num3][num4] + scr[i]);
if(num2 < num[2] and i - 2 >= 1)
f[i][num1][num2][num3][num4] = std::max(f[i][num1][num2][num3][num4],f[i-2][num1][num2+1][num3][num4] + scr[i]);
if(num3 < num[3] and i - 3 >= 1)
f[i][num1][num2][num3][num4] = std::max(f[i][num1][num2][num3][num4],f[i-3][num1][num2][num3+1][num4] + scr[i]);
if(num4 < num[4] and i - 4 >= 1)
f[i][num1][num2][num3][num4] = std::max(f[i][num1][num2][num3][num4],f[i-4][num1][num2][num3][num4+1] + scr[i]);
对于 \(100\%\) 的数据来说,这个 \(5\) 维的 \(dp\) 是完全卡不下的。
因为需要 \(3000MIB\) 的内存。
但是我们发现这个东西的第一维似乎只需要 \(4\) 个状态。
那么我们就可以开始滚了。
APJifengc:你可以滚了
然后我们如果使用 \(\%4\) 处理。
那么。。。
我们发现其实 \(4\) 是 \(2^2\),那么在取模的时候就可以直接对这个数字进行按位与 \(mod-1\)。
然后发现还是比较慢。
然后再经过大力卡常,之后就能过去了。
回头看了看题解,发现全都是四维的dp
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
auto max = [](int x,int y) {return x > y ? x : y;};
// #define int long long
namespace xin
{
int f[4][41][41][41][41];
int num[5];
int n,m;
int a[maxn],b[maxn];
int scr[maxn];
const int mod = 4;
inline short main()
{
io >> n >> m;
try(i,1,n) io >> scr[i];
try(i,1,m)
{
register int x; io >> x;
num[x] ++;
}
f[1][num[1]][num[2]][num[3]][num[4]] = scr[1];
try(i,2,n)
{
try(num1,0,num[1]) try(num2,0,num[2])
try(num3,0,num[3]) try(num4,0,num[4])
{
if(num1 xor num[1] and i - 1 >= 1)
f[i&(mod-1)][num1][num2][num3][num4] = max(f[i&(mod-1)][num1][num2][num3][num4],f[(i-1+mod)&(mod-1)][num1+1][num2][num3][num4] + scr[i]);
if(num2 xor num[2] and i - 2 >= 1)
f[i&(mod-1)][num1][num2][num3][num4] = max(f[i&(mod-1)][num1][num2][num3][num4],f[(i-2+mod)&(mod-1)][num1][num2+1][num3][num4] + scr[i]);
if(num3 xor num[3] and i - 3 >= 1)
f[i&(mod-1)][num1][num2][num3][num4] = max(f[i&(mod-1)][num1][num2][num3][num4],f[(i-3+mod)&(mod-1)][num1][num2][num3+1][num4] + scr[i]);
if(num4 xor num[4] and i - 4 >= 1)
f[i&(mod-1)][num1][num2][num3][num4] = max(f[i&(mod-1)][num1][num2][num3][num4],f[(i-4+mod)&(mod-1)][num1][num2][num3][num4+1] + scr[i]);
}
}
int ans = 0;
try(num1,0,num[1]) try(num2,0,num[2]) try(num3,0,num[3]) try(num4,0,num[4])
ans = max(ans,f[n&(mod-1)][num1][num2][num3][num4]);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
获得最最最差解,人均 100ms,我直接 3s
Day -15
比赛日。
然后多测没有清空
今天的 \(T1\) 还是比较友好的,只不过还是想了半个小时
之后的 \(T2\) 构造失败,多测。。。。
\(T3\) 只写了一个暴力,其实扫描线就很好。
\(T4\) 注意拓扑 \(ban\) 边的时候要先判断 if(!--ind[y])q.push(y)
。
所以这个破题连爆搜都没有打对。。。
谜之阶乘
我们发现阶乘连乘起来最多 \(20\) 就爆炸了。
然后因为这个的答案的区间一定是一段连续的。
所以我们直接对于 \({x}^\frac{1}{len}\)
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
int n;
class xin_data
{
public:
int x,y;
xin_data(){}
xin_data(int x,int y):x(x),y(y){}
}d[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(factorial);
#endif
// try(i,1,23) cout<<"i = "<<i<<" ans = "<<ans<<endl,ans *= i;
int T; io >> T;
while(T--)
{
io >> n; int zhi = 0;
if(n == 1) {printf("-1\n");continue;}
d[++zhi] = xin_data(n,n-1);
try(len,2,21)
{
int num = std::pow(n,1.0 / (double)len);
int st = std::max(1ll,num - len);
try(l,st,num)
{
int r = l + len - 1;
__int128 pi = 1;
try(i,l,r) pi *= i;
if(pi == n and l - 1) d[++zhi] = xin_data(r,l-1);
}
}
printf("%lld\n",zhi);
std::sort(d+1,d+zhi+1,[](const xin_data &x,const xin_data &y){return x.x < y.x;});
try(i,1,zhi) printf("%lld %lld\n",d[i].x,d[i].y);
}
return 0;
}
}
signed main() {return xin::main();}
子集
我们其实只用构造完前三行就好了。
所以考虑前三行的构造方案。
我们还是按照顺序填第一列,之后错一点填第二列,第三列就按照前面排序后的填。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
int n,k;
std::vector<int>ans[maxn];
class xin_data
{
public:
int x,id;
}d[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(subset);
#endif
int T; io >> T;
while(T--)
{
io >> n >> k;
if(n == 1 and k == 1) {printf("Yes\n1\n"); continue;}
if(n == k)
{
printf("No\n");
continue;
}
if(!((n / k) & 1))
{
int l = 1,r = n;
puts("Yes");
try(i,1,k)
{
try(j,l,l+(n/k/2)-1)
printf("%lld ",j);
try(j,r-(n/k/2)+1,r)
printf("%lld ",j);
printf("\n");
l = l + (n / k / 2);
r = r - (n / k / 2);
}
}
else
{
int zhi = 0;
int num = (1 + n) * n / 2 / k;
if(((1 + n) * n / 2) % k != 0) {puts("No"); continue;}
try(i,1,k) ans[i].clear();
try(i,1,k) ans[i].emplace_back(++zhi);
try(i,k/2+2,k) ans[i].emplace_back(++zhi);
try(i,1,k/2+1) ans[i].emplace_back(++zhi);
try(i,1,k) d[i].x = ans[i][0] + ans[i][1],d[i].id = i;
std::sort(d+1,d+k+1,[](const xin_data &x,const xin_data &y){return x.x > y.x;});
try(i,1,k) ans[d[i].id].emplace_back(++zhi);
while(1)
{
if(zhi >= n) break;
try(i,1,k) ans[i].emplace_back(++zhi);
if(zhi >= n) break;
throw(i,k,1) ans[i].emplace_back(++zhi);
}
printf("Yes\n");
try(i,1,k)
{
for(auto v : ans[i])
printf("%lld ",v);
printf("\n");
}
}
}
return 0;
}
}
signed main() {return xin::main();}
混凝土粉末
我们把每一个询问还有修改操作放到 \(1\) ~ \(n\) 的数轴上面。
之后我们用一个扫描线对于每一个操作进行修改。
之后如果有询问我们就在线段树上面二分就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
#define int long long
namespace xin
{
int n,qnum;
class xin_data
{
public:
int tim,h;
int type;
xin_data(){}
xin_data(int tim,int h,int type):tim(tim),h(h),type(type){}
};
std::vector<xin_data>vec[maxn],qsum[maxn];
class xin_seg
{
private:
#define ls(fa) (fa << 1)
#define rs(fa) (fa << 1 | 1)
public:
class xin_tree{public:int s;}t[maxn<<2];
int mx,res;
void upd(int fa,int l,int r,int pos,int val)
{
if(l == r) return t[fa].s += val,void();
register int mid = l + r >> 1;
if(pos <= mid) upd(ls(fa),l,mid,pos,val);
else upd(rs(fa),mid+1,r,pos,val);
t[fa].s = t[ls(fa)].s + t[rs(fa)].s;
}
void query(int fa,int l,int r,int qr)
{
if(~res or l>qr)return ;
if(r <= qr)
{
if(l == r)
{
if(t[fa].s >= mx) res = l;
else mx -= t[fa].s;
return ;
}
register int mid = l + r >> 1;
if(t[ls(fa)].s < mx) mx -= t[ls(fa)].s,query(rs(fa),mid+1,r,qr);
else query(ls(fa),l,mid,qr);
return ;
}
int mid=l+r>>1;
query(ls(fa),l,mid,qr); query(rs(fa),mid+1,r,qr);
}
}t;
int ans[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(concrete);
#endif
// freopen("t.txt","r",stdin);
io >> n >> qnum;
try(i,1,qnum)
{
register int op; io >> op;
if(op == 1)
{
register int l,r,h; io >> l >> r >> h;
// sb(l); jb(r);
vec[l].emplace_back(xin_data(i,h,1));
vec[r+1].emplace_back(xin_data(i,h,2));
}
else
{
register int x,y; io >> x >> y;
// sb(x); jb(y);
qsum[x].emplace_back(xin_data(i,y,0));
}
}
// debug;
try(i,1,n)
{
for(auto v : vec[i])
{
if(v.type == 1) t.upd(1,1,qnum,v.tim,v.h);
else t.upd(1,1,qnum,v.tim,-v.h);
}
for(auto v : qsum[i])
{
t.res = -1; t.mx = v.h;
t.query(1,1,qnum,v.tim);
ans[v.tim] = t.res;
}
}
try(i,1,qnum) if(ans[i])
{
if(ans[i] == -1) printf("0\n");
else printf("%lld\n",ans[i]);
}
return 0;
}
}
signed main() {return xin::main();}
附上线段树二分模板
线段树二分
void query(int fa,int l,int r,int qr)
{
if(~res or l>qr)return ;
if(r <= qr)
{
if(l == r)
{
if(t[fa].s >= mx) res = l;
else mx -= t[fa].s;
return ;
}
register int mid = l + r >> 1;
if(t[ls(fa)].s < mx) mx -= t[ls(fa)].s,query(rs(fa),mid+1,r,qr);
else query(ls(fa),l,mid,qr);
return ;
}
int mid=l+r>>1;
query(ls(fa),l,mid,qr); query(rs(fa),mid+1,r,qr);
}
排水系统
显然我们不能暴力将所有答案都算一遍,因此我们考虑整体统计。我们能观察到,若 \(x\) 点的排水管道堵塞,从 \(x\) 点经过的污水吨数必然不变。因此,我们可以借此考虑 \((x, y)\) 堵塞的影响:我们可以将管道堵塞视为 \(y\) 的经过污水吨数凭空减少一个定值,而 \(x\) 其他出点的污水吨数凭空增加一个定值。
而这个凭空增加、减少可以 \(x\) 吨的污水又可以视为初始这个点上有 \(x\) 或 \(−x\) 吨污水。因此,我们可以将题目转化回原始的情况,即没有边被堵塞时的情况。我们对此暴力统计,即可通过大部分特殊性质数据。而我们又发现,\(x\) 出点增加的定值可以视为加在 \(x\) 上并在 \(y\) 上减去,因此可以只修改 \(x, y\) 两个点上的信息。复杂度 \(\mathcal O(n+k)\)。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define int long long
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10;
namespace xin
{
const int mod = 998244353;
class xin_data
{
public:
int x,w;
xin_data(){}
xin_data(int x,int w):x(x),w(w){}
};
std::vector<xin_data>vec[maxn];
int n,m,r,k;
inline 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 ind[maxn],f[maxn];
int temp[maxn];
int ans[maxn];
inline short main()
{
file(water);
io >> n >> m >> r >> k;
int he = 0;
try(i,1,k)
{
register int x,y,z; io >> x >> y >> z; z %= mod;
vec[x].emplace_back(xin_data(y,z)); ind[y] ++;
he += z;
}
int inv = ksm(he,mod-2);
std::queue<int>q;
try(i,1,m) q.push(i),f[i] = 1;
memcpy(temp,ind,sizeof(int) * (n + 1));
while(q.size())
{
register int x = q.front(); q.pop();
int inv1 = ksm(vec[x].size(),mod-2),inv2 = ksm(vec[x].size()-1,mod-2);
// cout<<x<<endl;
// cout<<vec[x].size()<<endl;
for(auto v : vec[x])
{
register int y = v.x,val = v.w; --ind[y];
// cout<<val<<endl;
// cout<<y<<inv1<<inv2<<endl;
ans[x]=(ans[x]+(f[x]*((inv2-inv1+mod)%mod)%mod)*val%mod*inv%mod*vec[x].size()%mod)%mod;
ans[y]=(ans[y]-f[x]*inv1%mod*val%mod*inv%mod-(f[x]*((inv2-inv1+mod)%mod)%mod)*val%mod*inv%mod+mod*2ll)%mod;
f[y]=(f[y]+f[x]*inv1%mod)%mod;
// cout<<f[y]<<endl;
// cout<<ans[x]<<endl;
if(!ind[y]) q.push(y);
}
}
// try(i,1,n) cout<<ans[i]<<endl;
memcpy(ind,temp,sizeof(int) * (n + 1));
try(i,1,m) ans[i] ++,q.push(i),f[i] = 1;
while(q.size())
{
register int x = q.front(); q.pop();
int inv1 = ksm(vec[x].size(),mod-2);
for(auto v : vec[x])
{
register int y = v.x,val = v.w; --ind[y];
if(!ind[y]) q.push(y);
ans[y] = (ans[y] + ans[x] * inv1 % mod) % mod;
}
}
try(i,n-r+1,n) printf("%lld ",ans[i]);
putchar('\n');
return 0;
}
}
signed main() {return xin::main();}
Day -14
考的很爆炸。
\(dp\) 方程 \(naive\) 了,发现怎么也不对。
之后交了一个暴力就走人了。
这次又忘了 \(dp\) 写不出就加维这个原则了。
然后在 \(T1\) 上面搞了好长时间,结果后面的题目也草草写了暴力,也没有乱搞什么的。
其实 \(T3\) 乱搞的做法也是很快的啊。
总结:
- 即使没有写出正解,也不能影响下面的答题,下面的题目也还是有可能写出正解。
- 在暴力和部分分数打完之后,剩下知道一定会 \(TLE\) 的点打上乱搞比较好。
- \(dp\) 写不出要加维!!
回文
还是一个 \(palin\),只不过不太一样。
一直执着于二维坐标 \(dp\) 的转移,丝毫忘记了可以增加维度。
我们上来其实有一个 \(\mathcal O(n^4)\) 的想法就是我们直接设置 \(f_{x_1,y1,x_2,y2}\) 代表这个坐标的答案。
但是空间完全承受不住。
我们考虑减小维度。
发现其实只有步数一样的位置才是有效的。
所以这个就可以压缩成为三维 \(f_{k,x_1,x_2}\) 表示走了 \(k\) 步,然后第一个坐标的 \(x\) 为 \(x_1\),然后第二个为 \(x_2\)
这个就很好转移了。
我们确定了步数还有 \(x\),那么我们就可以确定 \(y\)
对于从左上开始的,那么就是 \(y_1 = len+2-x_1\),然后从右下开始的,就是 \(y_2 = n - x_2 + m - len\)。
之后枚举坐标愉快转移。
只有相同的位置可以转移。
话说然后就被卡常了。。。
循环展开真是一个好方法。。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
const int mod = 993244853;
int f[510][510][510];
char s[510][510];
int n,m;
const int dx1[] = {1,0},dy1[] = {0,1},dx2[] = {-1,0},dy2[] = {0,-1};
inline short main()
{
#ifdef ONLINE_JUDGE
file(palin);
#endif
io >> n >> m;
try(i,1,n) scanf("%s",s[i]+1);
f[0][1][n] = 1;
if(s[1][1] != s[n][m])
{
puts("0");
return 0;
}
if(n <= 490 and m <= 490)
{
try(len,0,(n+m-1)/2)
{
try(x1,1,n)
{
int y1 = len - x1 + 2;
if(!(x1 >= 1 and x1 <= n and y1 >= 1 and y1 <= m)) continue;
try(x2,1,n)
{
int y2 = n + m - x2 - len;
if(!(x2 >= 1 and x2 <= n and y2 >= 1 and y2 <= m) or s[x1][y1] != s[x2][y2]) continue;
try(t1,0,1)
{
int nx1 = x1 + dx1[t1],ny1 = y1 + dy1[t1];
if(!(nx1 >= 1 and nx1 <= n and ny1 >= 1 and ny1 <= m)) continue;
try(t2,0,1)
{
int nx2 = x2 + dx2[t2],ny2 = y2 + dy2[t2];
if(!(nx2 >= 1 and nx2 <= n and ny2 >= 1 and ny2 <= m)) continue;
if(s[nx1][ny1] == s[nx2][ny2]) (f[len+1][nx1][nx2] += f[len][x1][x2]) %= mod;
}
}
}
}
}
}
else
{
try(len,0,(n+m-1)/2)
{
try(x1,1,n)
{
int y1 = len - x1 + 2;
if(!(x1 >= 1 and x1 <= n and y1 >= 1 and y1 <= m)) continue;
try(x2,1,n)
{
int y2 = n + m - x2 - len;
if(!(x2 >= 1 and x2 <= n and y2 >= 1 and y2 <= m) or s[x1][y1] != s[x2][y2]) continue;
// try(t1,0,1)
{
int nx1 = x1 + dx1[0],ny1 = y1 + dy1[0];
if(!(nx1 >= 1 and nx1 <= n and ny1 >= 1 and ny1 <= m)) continue;
int nx2 = x2 + dx2[0],ny2 = y2 + dy2[0];
if(!(nx2 >= 1 and nx2 <= n and ny2 >= 1 and ny2 <= m)) continue;
if(s[nx1][ny1] == s[nx2][ny2]) (f[len+1][nx1][nx2] += f[len][x1][x2]) %= mod;
nx2 = x2 + dx2[1],ny2 = y2 + dy2[1];
if(!(nx2 >= 1 and nx2 <= n and ny2 >= 1 and ny2 <= m)) continue;
if(s[nx1][ny1] == s[nx2][ny2]) (f[len+1][nx1][nx2] += f[len][x1][x2]) %= mod;
}
{
int nx1 = x1 + dx1[1],ny1 = y1 + dy1[1];
if(!(nx1 >= 1 and nx1 <= n and ny1 >= 1 and ny1 <= m)) continue;
int nx2 = x2 + dx2[0],ny2 = y2 + dy2[0];
if(!(nx2 >= 1 and nx2 <= n and ny2 >= 1 and ny2 <= m)) continue;
if(s[nx1][ny1] == s[nx2][ny2]) (f[len+1][nx1][nx2] += f[len][x1][x2]) %= mod;
nx2 = x2 + dx2[1],ny2 = y2 + dy2[1];
if(!(nx2 >= 1 and nx2 <= n and ny2 >= 1 and ny2 <= m)) continue;
if(s[nx1][ny1] == s[nx2][ny2]) (f[len+1][nx1][nx2] += f[len][x1][x2]) %= mod;
}
}
}
}
}
int ans = 0;
int len = n + m - 2;
try(i,1,n) (ans += f[len/2][i][i]) %= mod;
if(len & 1) try(i,1,n-1) (ans += f[len/2][i][i+1]) %= mod;
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
写的麻烦了些。。。
快速排序
给了我一堆代码,之后就按照它说的去做。
然后因为 \(T1\) 调爆了,然后就真的按照他说的做了。。。
然后就暴力分数。
我们使劲观察一下子,发现其实这个模拟一遍是非常有规律的。
就是我们在排序的时候,其实他就是选择了最左边的那个数字为基准,然后在最左边的数字为 \(nan\) 的时候直接把 \(l\) 往右边挪了一个。
那么我们就可以开始找规律了。
其实每一个数字到最后都是排好序的,那么我们所需要的就是找到这个的每一个数字后面跟着的有多少个 \(nan\)。
因为对于每一个 \(l\) 为数字的排序递归,一定会把比这个数字小的东西放到左边,并且就不会再有 \(nan\) 跟着了。
所以我们刚开始就可以把每一个 \(nan\) 的位置用树状数组加上一个 \(1\),之后我们统计这个数和下一个比他大的数之间的 \(nan\) 的个数,这个就是它身后跟着的 \(nan\) 的个数。
所以还有一个单调栈
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
int n;
class xin_bit
{
private:
#define low(x) (x & -x)
public:
int c1[maxn],c2[maxn];
inline void add(int x,int val) {for(int i=x;i<=n;i+=low(i)) c1[i] += val,c2[i] += x * val;}
inline void upd(int l,int r,int val) {add(l,val); add(r+1,-val);}
inline int ask(int x) {int ret = 0; for(int i=x;i;i-=low(i)) ret += (x + 1) * c1[i] - c2[i]; return ret;}
inline int query(int l,int r) {return ask(r) - ask(l-1);}
}bit;
std::stack<int>st;
int a[maxn];
int mp[maxn],cnt = 0;
class xin_data
{
public:
int val,num,id;
}d[maxn];
char s[maxn];
int nxt[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(qsort);
#endif
int T; io >> T;
while(T--)
{
io >> n; cnt = 0;
try(i,1,n)
{
scanf("%s",s);
if(s[0] == 'n')
{
bit.upd(i,i,1);
a[i] = 0;
}
else
{
a[i] = atoi(s); mp[i] = ++cnt;
d[cnt].val = a[i]; d[cnt].id = i; d[cnt].num = 0;
while(st.size() and a[st.top()] <= a[i]) nxt[st.top()] = i,st.pop();
st.push(i);
}
}
// try(i,1,n) cout<<nxt[i]<<endl;
while(st.size()) nxt[st.top()] = n,st.pop();
int st = 1;
try(i,1,n) if(a[i]) break;
else
{
st ++;
printf("nan ");
}
int pre = 1;
try(i,st,n) if(a[i] >= pre)
{
d[mp[i]].num = bit.query(i,nxt[i]);
pre = a[i];
}
std::sort(d+1,d+cnt+1,[](const xin_data &x,const xin_data &y) {return (x.val == y.val) ? x.id < y.id : x.val < y.val;});
try(i,1,cnt)
{
printf("%d ",d[i].val);
while(d[i].num --) printf("nan ");
}
putchar('\n');
memset(bit.c1,0,sizeof(int) * (n + 1));memset(bit.c2,0,sizeof(int) * (n + 1));
}
return 0;
}
}
signed main() {return xin::main();}
混乱邪恶
这个题目是乱搞过得。
乱搞很简单,首先我们先一个空着一个得一个 \(-1\) 一个 \(1\),然后看差值。
之后我们每次进行修正,每一次都先 \(rand\) 出来一个位置,让其根据 \(tot\) 的大小进行变号。
之后就从 \(1\) 到 \(n\) 扫一遍,之后还是那样子加减。
复杂度真心不知道,但是还是非常快的。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int n,m;
int a[maxn];
int ans[maxn];
std::random_device shit;
inline int randint(int l,int r)
{
std::uniform_int_distribution<>e(l,r);
return e(shit);
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(chaoticevil);
#endif
io >> n >> m; int tot = 0;
try(i,1,n)
{
io >> a[i];
ans[i] = a[i] & 1;
if(ans[i]) tot += a[i];
else tot -= a[i];
}
// std::sort(a+1,a+n+1);
while(tot)
{
int pos = randint(1,n);
// jb(pos);
if(ans[pos]) tot -= a[pos] * 2,ans[pos] = 0;
else tot += a[pos] * 2,ans[pos] = 1;
if(tot > 0)
{
try(i,1,n)
{
if(tot <= 0) break;
if(ans[i]) tot -= a[i] * 2,ans[i] = 0;
}
}
else
{
try(i,1,n)
{
if(tot >= 0) break;
if(!ans[i]) tot += a[i] * 2,ans[i] = 1;
}
}
// jb(tot);
}
puts("NP-Hard solved");
try(i,1,n)
if(ans[i]) printf("1 ");
else printf("-1 ");
putchar('\n');
return 0;
}
}
signed main() {return xin::main();}
校门外歪脖树上的鸽子
不知道为啥题面这么奇怪。
我只写了一个大暴力。
就是还是模拟线段树的操作。
然后肯定 \(T\) 飞了。
但是还是有 \(48pts\) 算是不低的部分分数。
namespace _std还是很良心的,就是每天考试上来一个不是计数就是期望就很淦
[NOIP2018 提高组] 货币系统 | P5020
话说因为现在文件太长了,现在按退格都要好长时间。。。
这个题目之前看了老长时间了,然后看到标签是 \(dp\),然后就望而却步了。
今天做了一做,发现其实是一个结论题目。
一个似乎是显然的结论是这个新的东西一定会从原来的序列里面出来。
那么我们还有一个比较显然的结论就是说如果我们有一个 \(x\) 他可以被表示出来,那么 \(x-a_i\) 也是一定可以被表示出来。
那么事情就变得明朗起来了。
因为每一种货币都是无限多的,那么也就是说我们只要做一个可行的完全背包就好了。
然后对于答案如何统计,我们就直接先把答案赋值成为 \(n\),之后从 \(a_1\) 开始一个一个判断。
发现这个在之前被统计过了那么就直接 ans--
就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
int n;
int a[maxn];
int f[maxn];
inline short main()
{
int T; io >> T;
while(T--)
{
io >> n; int ans = n;
try(i,1,n) io >> a[i];
std::sort(a+1,a+n+1);
f[0] = 1;
try(i,1,n)
{
if(f[a[i]]) ans --;
try(j,a[i],a[n])
f[j] |= f[j - a[i]];
}
memset(f,0,sizeof(int) * (a[n] + 1));
printf("%d\n",ans);
}
return 0;
}
}
signed main() {return xin::main();}
Day -13
说实话,这一篇博客其实被我咕掉了。
但是在 \(Day \;-12\) 的时候来补了。。。
*破门而入
我不会告诉你我其实没看出来这个是第一类斯特林数。
然后我先是把这个题目放弃了,但是把 \(T2\) 切了之后就回来继续研究 \(T1\)了。
然后打了一个表,发现了这个 \(dp\) 公式。
所以我的 \(dp\) 就是通过打表推出来的
然后就没有什么了。。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int n,k;
int f[3010][3010];
inline short main()
{
#ifdef ONLINE_JUDGE
file(broken);
#endif
io >> n >> k;
f[1][1] = 1;
f[2][1] = 1; f[2][2] = 2;
try(i,3,n)
{
if(i-1) f[i][1] = f[i-1][1] * (i - 1) % mod;
try(j,2,i-1)
{
f[i][j] = f[i-1][j] * (i - 1) % mod + f[i-1][j-1];
f[i][j] %= mod;
}
f[i][i] = f[i-1][i-1] * i % mod;
}
cout<<f[n][k]<<endl;
return 0;
}
}
signed main() {return xin::main();}
翻转游戏
说实话这个题目真的是水,直接发现如果前面没有出现过这个字母的话,那么我们直接加上 \(i-1\) 的贡献。
然后如果出现过的话,那么也就是直接加上前面和这个字母不一样的字母的个数就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
char s[maxn];
ll ans = 0;
class xin_bit
{
private:
#define low(x) (x & -x)
public:
int c[101];
inline void add(int x) {for(;x<=26;x+=low(x)) ++ c[x];}
inline int query(int x) {int ret = 0; for(;x;x-=low(x)) ret += c[x]; return ret;}
}bit;
inline short main()
{
#ifdef ONLINE_JUDGE
file(turn);
#endif
scanf("%s",s+1);
int n = strlen(s+1); ans = 1;
try(i,1,n)
{
// try(j,1,26) he[i][j] = he[i-1][j];
int temp = bit.query(s[i] - 'a' + 1) - bit.query(s[i] - 'a');
if(!temp)
ans += i - 1;
else
{
// try(j,1,26) if(s[i] - 'a' + 1 != j)
// ans += he[i][j];
ans += bit.query(26) - temp;
}
// he[i][s[i] - 'a' + 1]++;
bit.add(s[i]-'a'+1);
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
奶油蛋糕塔
这个发现其实一共就只有 \(10\) 中的蛋糕形态。
那么我们对于同一种的蛋糕形态,直接把他们合并成为一个,这样一共就只剩下 \(10\) 个蛋糕了。
之后爆搜就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int u[] = {0,0,0,0,1,1,1,2,2,3},v[] = {0,1,2,3,1,2,3,2,3,3};
int id[10][10];
int n;
std::vector<int>vec[1001];
int tot[101];
int fa[maxn];
inline int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
inline void merge(int x,int y) {fa[find(x)] = find(y);}
int temp[maxn],a[maxn],ans;
inline void check()
{
// try(i,1,10) cout<<a[i]<<' '; cout<<endl;
try(i,0,3) fa[i] = i,temp[i] = 0;
int ret = 0,cnt = 0,odd = 0;
try(i,0,9)
{
cnt = vec[i].size() - a[i];
if(!cnt) continue;
ret += tot[i];
int x = u[i],y = v[i];
temp[x] += cnt; temp[y] += cnt;
merge(x,y);
if(a[i] == 1) ret -= vec[i][0];
else if(a[i] == 2) ret -= vec[i][0] + vec[i][1];
}
try(i,0,3) odd += (temp[i] & 1);
if(odd > 2 or odd == 1) return ; int res = -1;
try(i,0,3) if(temp[i])
{
if(res == -1) res = i;
else if(find(res) != find(i)) return ;
}
ans = std::max(ans,ret);
}
void dfs(int x)
{
if(x == 10)
{
check();
return ;
}
a[x] = 0; dfs(x + 1);
if(vec[x].size())
{
a[x] = 1; dfs(x+1);
if(vec[x].size() >= 2) a[x] = vec[x].size(),dfs(x+1);
}
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(cake);
#endif
io >> n;
id[0][0] = 0; id[0][1] = 1; id[0][2] = 2; id[0][3] = 3;
id[1][1] = 4; id[1][2] = 5; id[1][3] = 6; id[2][2] = 7;
id[2][3] = 8; id[3][3] = 9;
try(i,1,n)
{
int val; char s[10];
io >> val; scanf("%s",s+1);
int l = s[1] - 'W',r; scanf("%s",s+1);
r = s[1] - 'W';
if(l > r) std::swap(l,r);
vec[id[l][r]].push_back(val);
tot[id[l][r]] += val;
// cout<<a[l][r]<<endl;
}
try(i,1,9) std::sort(vec[i].begin(),vec[i].end());
dfs(0);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
多重影分身
直接二分答案然后 \(\mathcal O(n)\;check\) 就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int u[] = {0,0,0,0,1,1,1,2,2,3},v[] = {0,1,2,3,1,2,3,2,3,3};
int id[10][10];
int n;
std::vector<int>vec[1001];
int tot[101];
int fa[maxn];
inline int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
inline void merge(int x,int y) {fa[find(x)] = find(y);}
int temp[maxn],a[maxn],ans;
inline void check()
{
// try(i,1,10) cout<<a[i]<<' '; cout<<endl;
try(i,0,3) fa[i] = i,temp[i] = 0;
int ret = 0,cnt = 0,odd = 0;
try(i,0,9)
{
cnt = vec[i].size() - a[i];
if(!cnt) continue;
ret += tot[i];
int x = u[i],y = v[i];
temp[x] += cnt; temp[y] += cnt;
merge(x,y);
if(a[i] == 1) ret -= vec[i][0];
else if(a[i] == 2) ret -= vec[i][0] + vec[i][1];
}
try(i,0,3) odd += (temp[i] & 1);
if(odd > 2 or odd == 1) return ; int res = -1;
try(i,0,3) if(temp[i])
{
if(res == -1) res = i;
else if(find(res) != find(i)) return ;
}
ans = std::max(ans,ret);
}
void dfs(int x)
{
if(x == 10)
{
check();
return ;
}
a[x] = 0; dfs(x + 1);
if(vec[x].size())
{
a[x] = 1; dfs(x+1);
if(vec[x].size() >= 2) a[x] = vec[x].size(),dfs(x+1);
}
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(cake);
#endif
io >> n;
id[0][0] = 0; id[0][1] = 1; id[0][2] = 2; id[0][3] = 3;
id[1][1] = 4; id[1][2] = 5; id[1][3] = 6; id[2][2] = 7;
id[2][3] = 8; id[3][3] = 9;
try(i,1,n)
{
int val; char s[10];
io >> val; scanf("%s",s+1);
int l = s[1] - 'W',r; scanf("%s",s+1);
r = s[1] - 'W';
if(l > r) std::swap(l,r);
vec[id[l][r]].push_back(val);
tot[id[l][r]] += val;
// cout<<a[l][r]<<endl;
}
try(i,1,9) std::sort(vec[i].begin(),vec[i].end());
dfs(0);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day -12
炸没了。。。
\(T1\) 直接因为对拍数据范围没有想好,然后到最后都是一直 \(ac\),然后直接挂没 \(100pts\)。
之后 \(T3\),还写挂了,一个破暴力竟然保龄了?!
本以为自己的 \(T4\) 也可以过掉,然后发现实现也不对?!!
估分 \(300pts\) 实际上 \(40pts\) ?!
淦淦淦
石子合并
这个要注意的就是对于正负数据的判断。
之后我们一定就可以构造出一种方案使得只变动最大最小值的状态然后使答案最大。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 7e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
auto max = [](int x,int y) {return x > y ? x : y;}; auto min = [](int x,int y) {return x > y ? y : x;};
namespace xin
{
int n;
int a[maxn];
inline int abs(int x) {return x < 0 ? -x : x;}
inline short main()
{
#ifdef ONLINE_JUDGE
file(stone);
#endif
int T; io >> T;
while(T--)
{
io >> n; int he = 0;
int ok1 = 0,ok2 = 0;
try(i,1,n)
{
io >> a[i],he += a[i];
if(a[i] > 0) ok1 = 1;
if(a[i] < 0) ok2 = 1;
}
if(n == 1) {printf("%lld\n",a[1]); continue;}
int ans = -inf;
if(ok2 == 0 and ok1)
{
// debug;
try(i,1,n-1)
ans = std::max(ans,std::max(a[i],a[i+1]) - std::min(a[i],a[i+1]) + he - a[i] - a[i + 1]);
}
else if(ok1 == 0 and ok2)
{
ans = 0;
try(i,1,n) ans += abs(a[i]);
int maxx = -inf;
try(i,1,n) maxx = std::max(maxx,a[i]);
// cout<<ans + 2 * maxx<<endl;
ans += 2 * maxx;
}
else
{
// debug;
ans = 0;
try(i,1,n) ans += abs(a[i]);
}
cout<<ans<<endl;
}
return 0;
}
}
signed main() {return xin::main();}
翻转游戏
我们这个要求的是区间的交!!!
而不是并!!
所以其实我们对于坐标取 \(max\) 和 \(min\) 就好了。
然后容斥一下。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
auto max = [](int x,int y) {return x > y ? x : y;}; auto min = [](int x,int y) {return x > y ? y : x;};
namespace xin
{
class xin_data
{
public:
int x1,y1,x2,y2;
xin_data(){}
xin_data(int x1,int y1,int x2,int y2):x1(x1),y1(y1),x2(x2),y2(y2){}
inline xin_data cross(const xin_data &x)
{
if(x1 == -1 or x.x1 == -1) return xin_data(-1,0,0,0);
xin_data ret = xin_data(std::max(x1,x.x1),std::max(y1,x.y1),std::min(x2,x.x2),std::min(y2,x.y2));
if(ret.x1 >= ret.x2 or ret.y1 >= ret.y2) ret.x1 = -1;
return ret;
}
inline int get() {return (x2 - x1) * (y2 - y1);}
}d[maxn],pre[maxn],suf[maxn];
int n,q,p;
inline void work()
{
io >> p >> q >> n;
int ans = 0;
try(i,1,n) io >> d[i].x1 >> d[i].y1 >> d[i].x2 >> d[i].y2;
pre[0] = suf[n+1] = xin_data(0,0,p,q);
try(i,1,n) pre[i] = pre[i-1].cross(d[i]);
throw(i,n,1) suf[i] = suf[i+1].cross(d[i]);
try(i,1,n)
{
xin_data temp = pre[i-1].cross(suf[i+1]);
if(~temp.x1) ans += temp.get();
}
if(~suf[n].x1) ans -= (n - 1) * pre[n].get();
cout<<ans<<endl;
}
inline short main()
{
file(carpet);
int T; io >> T;
while(T--) work();
return 0;
}
}
signed main(){return xin::main();}
优美的旋律
但凡学过 \(hash\) 的人都应该会这个题目!!!
但是我保龄了!!!
炸了!!!
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
auto max = [](int x,int y) {return x > y ? x : y;}; auto min = [](int x,int y) {return x > y ? y : x;};
namespace xin
{
int k1,k2;
char s[maxn];
int n;
ull hash[maxn],p[maxn];
inline ull get(int l,int r) {return hash[r] - hash[l-1] * p[r- l + 1];}
int ans = 0;
inline short main()
{
#ifdef ONLINE_JUDGE
file(melody);
#endif
io >> k1 >> k2;
scanf("%s",s+1);
n = strlen(s+1);
p[0] = 1;
try(i,1,n) hash[i] = hash[i-1] * 13331 + (s[i] - 'a' + 1),p[i] = p[i-1] * 13331;
try(l,1,n)
{
try(len,1,n-l)
{
ull st = get(l,l+len-1);
int cnt = 0;
for(int r = l + len - 1;r <= n;r += len)
{
if(get(r - len + 1,r) == st) cnt ++;
else break;
}
if(cnt != 1)ans = std::max(ans,len * k1 + cnt * k2);
// sb(len); sb(cnt); jb(len * k1 + cnt * k2);
}
}
cout<<ans<<endl;
return 0;
}
}
signed main(){return xin::main();}
基站建设
正解并非如此,但我是枚举第一个然后枚举度数之后选择最大的两个来做的。
之后还非常快。。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
auto max = [](int x,int y) {return x > y ? x : y;}; auto min = [](int x,int y) {return x > y ? y : x;};
namespace xin
{
std::bitset<50001>bit[50001],temp;
std::vector<int>vec[50001];
int n,m;
class xin_data
{
public:
int x,y;
}d[maxn];
int r[maxn],a[maxn];
int ans = 0;
inline short main()
{
#ifdef ONLINE_JUDGE
file(station);
#endif
io >> n >> m;
try(i,1,n) io >> r[i];
try(i,1,m)
{
register int x,y; io >> x >> y;
vec[x].emplace_back(y); vec[y].emplace_back(x);
bit[x][y] = 1; bit[y][x] = 1;
}
try(i,1,n) std::sort(vec[i].begin(),vec[i].end(),[&](int x,int y) {return r[x] > r[y];});
try(i,1,n) if(vec[i].size() >= 2)
{
for(auto v1 : vec[i])
{
int cnt = 0;
for(auto v2 : vec[i]) if(v1 != v2)
{
if(bit[v1][v2]) a[++cnt] = v2;
if(cnt == 2) break;
}
if(cnt < 2) continue;
ans = std::max(ans,(r[i] + 1) * (r[v1] + 1) + r[a[1]] * r[a[2]]);
// debug;
// sb(a[1]); jb(a[2]);
// jb((r[i] + 1) * (r[v1] + 1) + r[a[1]] * r[a[2]]);
}
}
cout<<ans<<endl;
return 0;
}
}
signed main(){return xin::main();}
Day -11
话说你们可能不相信
我改完题了。。
改了一题
NOIP 2018
被卡常了。
首先发现答案是有单调性的。
然后我们就可以二分答案。
关键就是该怎么去 \(check\)。
其实有一个显然的 \(loglog\) 的做法。
然后应该是过不去。
然后经过 \(APJifengc\) 的 \(nb\) 卡常三分做法。
然后比他的 \(log\) 快
就是在固定答案之后。
容易发现这个东西是一个二次函数,之后三分购买第一个的个数。
然后所以我们找到最低的点就能判断这个答案是否可行。
那么直接一个三分就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int a,b,c,d;
int x;
// auto ws = [](int x) {int ret = 0;while(x) ret ++,x /=10; return ret;};
inline auto get1(int x)
{
__uint128_t ret = x - 1; ret *= x; ret /= 2; ret *= b;
__uint128_t temp = a; temp *= x; ret += temp;
return ret;
// return (x - 1) * x / 2 * b + a * x;
}
inline auto get2(int x)
{
__uint128_t ret = x - 1; ret *= x; ret /= 2; ret *= d;
__uint128_t temp = c; temp *= x; ret += temp;
return ret;
// return (x - 1) * x / 2 * d + c * x;
}
// inline int wei1(int x) {return ws(x) * 2 + ws(b);}
// inline int wei2(int x) {return ws(x) * 2 + ws(b);}
auto check = [](int k) -> bool
{
// int temp2 = (2 * a - b - d * k - 2);
// int temp1 = b / 2;
// int ret = -(temp2 / (temp1));
// return get1(ret) + get2(k - ret) <= x;
int l = 0,r = k;
// if((get1(0) + get2(k) <= x) or (get1(k) + get2(0) <= x)) return 1;
while(r - l >= 10)
{
// register int mid1 = (r - l + 1) / 3 + l,mid2 = r - (r - l + 1) / 3;
register int mid1 = ((l + r) >> 1) , mid2 = mid1 + 1;
auto com1 = get1(mid1) + get2(k - mid1),com2 = get1(mid2) + get2(k - mid2);
if(com1 > com2)
{
if(com2 <= x) return 1;
l = mid1;
}
else
{
if(com1 <= x) return 1;
r = mid2;
}
}
try(i,l,r)
{
if(get1(i) + get2(k - i) <= x) return 1;
}
return 0;
};
int wb;
auto ws = [](int x) {int ret = 0;while(x) ret ++,x /=10; return ret;};
auto check1 = [](int k) -> bool
{
if(ws(k) * 2 + wb >= 20) return 1;
__int128 ret = b; ret *= k; ret *= k;
ret += (2 * a - b) * k;
return ret > x;
};
inline short main()
{
#ifdef ONLINE_JUDGE
file(money);
#endif
int T; io >> T;
while(T--)
{
io >> a >> b >> c >> d >> x;
int cnt1 = 0,cnt2 = 0;
if((x / b <= (int)(1e7) or x / d <= (int)(1e7)) and d >= 5 and b >= 5)
{
// debug;
while(x >= 0)
{
while(x >= 0 and b * cnt1 + a <= d * cnt2 + c)
x -= b * cnt1 + a,cnt1 ++;
while(x >= 0 and b * cnt1 + a > d * cnt2 + c)
x -= d * cnt2 + c,cnt2 ++;
}
// sb(cnt1); jb(cnt2);
printf("%lld\n",cnt1 + cnt2 - 1);
continue;
}
int l = 0,r = inf * 2;
while(l != r - 1 and l < r)
{
register int mid = l + r >> 1;
if(check(mid)) l = mid;
else r = mid;
}
if(check(r)) printf("%lld\n",r);
else printf("%lld\n",l);
}
return 0;
}
}
signed main() {return xin::main();}
Day -10
P1941 [NOIP2014 提高组] 飞扬的小鸟
这个题目首先发现这个答案是有单调性的。
那么我们的想法就是可以先二分答案。
然后发现做不出来
所以不是二分答案。。。
那么我们换一种思路,发现每次按上键的次数是无限的,这个似乎就相当于一个完全背包。
然后只能最多有一次掉落,那么也就是相当于 \(01\) 背包。
那么其实这个题目也就非常明朗了。
设 \(f_{i,j}\) 表示走到 \(i\) 这个坐标,之后高度为 \(j\) 的最小次数。
那么:
最后的这个方程就是表示最大的高度只能是 \(m\)。
之后我们考虑有管道的情况,这个直接设置成为 \(\inf\) 就行了
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
int n,m,k;
class xin_data
{
public:
int x,xia,shang;
}d[maxn];
int f[2][maxn];
int up[maxn],down[maxn];
inline int min(int x,int y) {return x > y ? y : x;}
inline short main()
{
io >> n >> m >> k;
try(i,1,n) io >> up[i] >> down[i];
try(i,1,k) io >> d[i].x >> d[i].xia >> d[i].shang;
std::sort(d+1,d+k+1,[&](const xin_data &x,const xin_data &y) {return x.x < y.x;});
int zhi = 1;
try(i,1,n)
{
int cur = i & 1,lst = cur ^ 1;
memset(f[cur],0x3f,sizeof(int) * (m + 1));
try(j,up[i]+1,up[i]+m) f[cur][j] = min(f[cur][j-up[i]],f[lst][j-up[i]]) + 1;
// for(int j=up[i]+1;j<=up[i]+m;j++)//p=1,完全背包
// f[i%2][j]=min(f[i%2^1][j-up[i]]+1,f[i%2][j-up[i]]+1);
try(j,m+1,m+up[i]) f[cur][m] = min(f[cur][m],f[cur][j]);
try(j,1,m-down[i]) f[cur][j] = min(f[cur][j],f[lst][j+down[i]]);
if(i == d[zhi].x)
{
try(j,0,d[zhi].xia) f[cur][j] = inf;
throw(j,m,d[zhi].shang) f[cur][j] = inf;
int ans = inf;
try(j,1,m) ans = min(ans,f[cur][j]);
if(ans >= inf)
{
printf("0\n%d\n",zhi - 1);
return 0;
}
++zhi;
}
// jb(i);
// try(j,1,m) cout<<f[cur][j]<<' ';
// cout<<endl;
}
int ans = inf;
try(j,1,m) ans = min(ans,f[n & 1][j]);
cout<<1<<endl<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
CF 1572C
关于区间上的问题,还是考虑区间 \(dp\) 来解决。
我们发现一个连续的长串的相同颜色是没有什么用的。
所以直接把序列去重就行了。
之后我们考虑一个结论:
我们把整个序列都变成两个端点上面的其中一个颜色一定不劣。
证明:
因为端点是和其他的点没有什么关系的,所以无论前面的怎么搞,最后的端点一定还是要变色。
那么不如在搞掉前面所有点的颜色之后直接再把前面的颜色都变成端点的颜色。
有了这个结论,那么其实这个题目就简单很多了, \(dp\) 方程也是从这个上面转过来的。
我们设 \(f_{i,j}\) 表示 \(i\) 到 \(j\) 这个区间,之后把这个区间都变成 \(c_j\) 这个颜色的最小次数。
那么:$$f_{i,j} = \min(f_{i+1,j},f_{i,j-1})+1$$。
之后还有一种情况,就是说如果这个序列当中有相同的颜色,那么这个就不是最优的。
这时候只要找到那个相同的颜色的上一个,位置记为 \(k\),那么 \(f_{i,j} = \min(f_{i,k}+f_{k+1,j})\)。
这样就能保证最小了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
int f[3010][3010];
int a[maxn],pre[maxn];
int vis[maxn];
inline short main()
{
int T; io >> T;
while(T--)
{
int m,n = 0; io >> m;
try(i,1,m) io >> a[i],pre[a[i]] = vis[a[i]] = 0;
n = std::unique(a+1,a+m+1) - (a + 1);
// try(i,1,n) cout<<a[i]<<' '; cout<<endl;
try(i,1,n) pre[i] = vis[a[i]],vis[a[i]] = i;//,sb(i),jb(pre[i]);
try(i,1,n) try(j,1,n) f[i][j] = inf;
try(i,1,n) f[i][i] = 0,f[i+1][i] = 0;
try(len,2,n)
{
try(i,1,n-len+1)
{
register int j = i + len - 1;
f[i][j] = std::min(f[i+1][j],f[i][j-1]) + 1;
for(int k=pre[j];k>=i;k=pre[k])
f[i][j] = std::min(f[i][j],f[i][k]+f[k+1][j]);
}
}
// try(i,1,n) try(j,i,n) sb(i),sb(j),jb(f[i][j]);
printf("%d\n",f[1][n]);
}
return 0;
}
}
signed main() {return xin::main();}
P2822 [NOIP2016 提高组] 组合数问题
其实本来以为这个东西有一个很厉害的结论的。
但是其实数据范围很小,\(n^2\) 递推加 \(\mathcal O(1)\) 回答即可。
首先暴力的做法还是比较显然的,既然我们要求能够整除 \(k\) 的组合数,那么我们的思路一定就是直接 \(mod\;k\),之后为 \(0\) 的就是有贡献的。
那么因为只有 \(2000\) 我们预处理出来所有的组合数之后取前缀和。
因为模数很可能不是质数,所以我们要用杨辉三角。
还有要注意的是这个预处理出来的是一个三角矩阵,计算前缀和的时候我们要保证 \(!c_{i,j}\;and\;j\leq i\)
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
int c[2010][2010];
int he[2010][2010];
int k,n,m;
inline short main()
{
int T; io >> T >> k;
try(i,0,2000)
{
c[i][0] = 1;
try(j,1,i) c[i][j] = c[i-1][j] + (c[i-1][j-1]),c[i][j] %= k;
}
try(i,1,2000)
{
try(j,1,2000)
he[i][j] = he[i-1][j] + he[i][j-1] - he[i-1][j-1] + (!c[i][j] and j <= i);
}
// jb(he[2000][2000]);
while(T--)
{
io >> n >> m;
m = std::min(n,m);
printf("%d\n",he[n][m]);
}
return 0;
}
}
signed main() {return xin::main();}
P1220 关路灯
一个上来感觉是背包,但是实际上还是一个区间 \(dp\) 的题目。
范围 \(n\leq 50\),其实这个范围感觉很迷,丝毫不知道从哪里下手。
但是就是完全不能够爆搜
我们设 \(f_{i,j}\) 为关闭了 \(i\) 到 \(j\) 的电灯的答案。
那么就很显然 \(f_{1,n}\) 就是我们的目标。
因为要取 \(\min\) 所以自然 \(f_{pos,pos}=0\)。
但是其实这个样子是没有办法去转移的。
那么我们再加维。
\(f_{i,j,0/1}\) 这个就代表关完这些灯之后老王在左(0)右(1)。
那么转移就显然了。
f[i][j][0]=std::min(f[i+1][j][0] + (a[i+1] - a[i]) * (he[n] - (he[j] - he[i])),f[i+1][j][1] + (a[j] - a[i]) * (he[n] - (he[j] - he[i]))));
f[i][j][1]=std::min(f[i][j-1][0] + (a[j] - a[i]) * (he[n] - (he[j-1] - he[i-1])),f[i][j-1][1] + (a[j] - a[j-1]) * (he[n] - (he[j-1] - he[i-1]))));
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
void chkmax(int &x,int y) {x = x > y ? x : y;} void chkmin(int &x,int y) {x = x > y ? y : x;}
namespace xin
{
int n,pos;
int f[51][51][2];
int a[maxn],b[maxn];
int he[maxn];
inline short main()
{
io >> n >> pos;
try(i,1,n) io >> a[i] >> b[i],he[i] = he[i-1] + b[i];
memset(f,0x3f,sizeof(f));
f[pos][pos][1] = f[pos][pos][0] = 0;
try(len,2,n)
{
try(i,1,n - len + 1)
{
int j = i + len - 1;
chkmin(f[i][j][0],std::min(f[i+1][j][0] + (a[i+1] - a[i]) * (he[n] - (he[j] - he[i])),f[i+1][j][1] + (a[j] - a[i]) * (he[n] - (he[j] - he[i]))));
chkmin(f[i][j][1],std::min(f[i][j-1][0] + (a[j] - a[i]) * (he[n] - (he[j-1] - he[i-1])),f[i][j-1][1] + (a[j] - a[j-1]) * (he[n] - (he[j-1] - he[i-1]))));
}
}
cout<<std::min(f[1][n][1],f[1][n][0])<<endl;
return 0;
}
}
signed main() {return xin::main();}
复习了欧拉函数的线性筛法。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
void chkmax(int &x,int y) {x = x > y ? x : y;} void chkmin(int &x,int y) {x = x > y ? y : x;}
namespace xin
{
int phi[maxn],pri[maxn];
bool vis[maxn];
int tot;
inline void shai(int ms)
{
phi[1] = 1;
try(i,2,ms)
{
if(!vis[i]) pri[++tot] = i,phi[i] = i - 1;
for(int j=1;pri[j] * i <= ms and j <= tot;++j)
{
vis[pri[j] * i] = 1;
if(i % pri[j]) phi[i * pri[j]] = phi[i] * phi[pri[j]];
else{phi[i * pri[j]] = phi[i] * pri[j]; break;}
}
}
}
inline short main()
{
return 0;
}
}
signed main() {return xin::main();}
[NOIP2014 提高组] 解方程 | P2312
其实这个题目就是唬人,并不是很难。
发现输入就是一个高精度的数,但是其实我们只需要它 \(mod\) 上一个数就好了。
那么就很好办。
之后我们发现其实 \(m\) 就有 \(10^6\),直接暴力枚举,然后判断。
\(n\) 虽然只有 \(100\),但是如果这个 \(n\) 次多项式如果使用快速幂暴力计算的话,复杂度直接无法接受。
所以我们把这个多项式试图优化成 \(\mathcal O(n)\) 计算。
如何优化???
那么最后从 \(n\) 到 \(1\) 直接就能 \(\mathcal O(n)\) 计算了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch - '0'),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int n,m;
int a[maxn];
char s[maxn];
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
inline bool calc(int x)
{
int ret = 0;
throw(i,n,1) ret = (ret + a[i]) * x % mod;
return !((ret + a[0]) % mod);
// int ret = a[0];
// try(i,1,n) ret += (a[i] * ksm(x,i) % mod),ret -= (ret >= mod) ? mod : 0;
// return !ret;
}
int ans[maxn],zhi = 0;
inline short main()
{
io >> n >> m;
// sb(n); jb(m);
try(i,0,n)
{
scanf("%s",s+1);
int len = strlen(s + 1);
int ret = 0;
if(s[1] == '-')
{
try(j,2,len) ret = ret * 10 + (s[j] - '0'),ret %= mod;
a[i] = (-ret + mod) % mod;
// jb(a[i]);
}
else
{
try(j,1,len) ret = ret * 10 + (s[j] - '0'),ret %= mod;
a[i] = ret;
// jb(a[i]);
}
// sb(i); jb(a[i]);
}
// debug;
try(i,1,m) if(calc(i)) ans[++zhi] = i;
printf("%lld\n",zhi);
try(i,1,zhi)
printf("%lld\n",ans[i]);
return 0;
}
}
signed main() {return xin::main();}
复习了高斯消元,发现还是有一些细节打不对。。。
try(j,1,n) if(i xor j)
{
if(fabs(a[i][i]) <= 1e-8) return !printf("No Solution");
double temp = a[j][i] / a[i][i];
try(k,i,n+1)
{
a[j][k] -= a[i][k] * temp;
}
}
每次去消的应该是 \(j\) 行,而不是 \(i\) 行。
Day -9
仅有 \(9\) 天了。
开挂
我们其实能够直接确定这是一个贪心。
数据范围很大,所以最多带一个 \(log\)。
排序是一定需要的,之后我们就是要找到最小的方案。
这样我们只要让这些东西的差值乘上最小的 \(b\),就好了。
用三个栈维护一下就能在这一部分做到 \(\mathcal O(n)\) 了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int a[maxn],b[maxn];
int n;
class xin_data
{
public:
int x,y;
}d[maxn];
int cnt = 0;
int st1[maxn],st2[maxn],st3[maxn];
int zhi1,zhi2,zhi3;
int tot = 0;
inline short main()
{
#ifdef ONLINE_JUDGE
file(openhook);
#endif
io >> n;
try(i,1,n) io >> a[i]; try(i,1,n) io >> b[i];
std::sort(a+1,a+n+1); std::sort(b+1,b+n+1);
a[n+1] = INT_MAX; int size = 0;
try(i,1,n) if(a[i] == a[i+1])
{
st1[++zhi1] = a[i];
++size;
}
else
{
if(!zhi1) continue;
if(a[i] + 1 != a[i+1])
{
if(a[i+1] - a[i] - 1 > size)
{
try(j,a[i]+1,a[i]+size) st2[++zhi2] = j;
size = 0;
st3[++zhi3] = st2[zhi2--];
while(zhi1)
{
while(zhi2 and st1[zhi1] < st2[zhi2])
{
st3[++zhi3] = st2[zhi2--];
}
d[++cnt] = xin_data{st1[zhi1--],st3[zhi3--]};
}
}
else
{
try(j,a[i]+1,a[i+1]-1) st2[++zhi2] = j;
size -= (a[i+1] - a[i] - 1);
}
}
}
// jb(cnt);
// try(i,1,cnt) cout<<d[i].x<<' '<<d[i].y<<endl;
std::sort(d+1,d+cnt+1,[&](const xin_data &x,const xin_data &y){return x.y - x.x > y.y - y.x;});
ull ans = 0;
// jb(zhi1);
// jb(cnt);
try(i,1,cnt) ans += 1ull * (d[i].y - d[i].x) * b[i];
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
叁仟柒佰万
也是一个 \(\mathcal O(n)\) 的题目,只不过这个是一个 \(dp\)。
我们考虑整个序列所能构成的合法的方案的 \(mex\) 一定会是唯一的。
所以我们直接找前面 \(mex\) 值等于那个 \(K\) 的 \(f_j\) 来转移就好了。
这样是 \(\mathcal O(n^2)\) 的,取一个前缀和优化成 \(\mathcal O(n)\)
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 4e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
const int mod = 1e9+7;
int f[maxn];
int a[maxn];
int n;
int mex = 0;
int vis[maxn];
#define fmod(x) (x -= (x >= mod) ? mod : 0)
inline short main()
{
#ifdef ONLINE_JUDGE
file(clods);
#endif
int T; io >> T;
while(T--)
{
io >> n; mex = 0;
if(n == 37000000)
{
int x,y; io >> x >> y;
a[1] = 0; vis[0] ++;
try(i,2,n)
{
a[i] = (a[i-1] * x + y + i) & 262143;
vis[a[i]] ++;
}
}
else
{
memset(vis,0,sizeof(int) * (n + 2));
try(i,1,n) io >> a[i],vis[a[i]] ++,f[i] = 0;
}
while(vis[mex]) mex ++;
memset(vis,0,sizeof(int) * (mex));
try(i,mex+1,n) vis[i] = n + 1;
f[0] = 1;
int l = 1,temp = 0;
try(i,1,n)
{
if(!vis[a[i]]) ++ temp;
vis[a[i]] ++;
if(temp == mex)
{
for(;(l xor i) and vis[a[l]] > 1;++l)
vis[a[l]] --;
f[i] = f[i-1] + f[l-1];
fmod(f[i]);
}
else f[i] = f[i-1];
}
cout<<(f[n] - f[n-1] + mod) % mod<<endl;
}
return 0;
}
}
signed main() {return xin::main();}
Day -8
[TJOI2013] 奖学金 | P3963
这个求中位数的题目我们考虑向左右塞数。
首先想到了一个二分答案的思路,发现 \(naive\) 得一批。
之后发现其实我们只要预处理出来每一个数作为中位数的代价就可以很快得到答案。
考虑用堆来维护这个值,见到大的就弹出来,保证里面有 \(\frac{n}{2}\) 个数并且最小就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
class xin_data
{
public:
int x,y;
}d[maxn];
int n,c,f;
std::priority_queue<int>q1,q2;
int he1[maxn],he2[maxn];
inline short main()
{
io >> c >> n >> f;
try(i,1,n) io >> d[i].x >> d[i].y;
std::sort(d+1,d+n+1,[&](const xin_data &x,const xin_data &y){return x.x < y.x;});
int l = c / 2 + 1,r = n - c / 2;
int sum = 0;
// try(i,1,n) sb(i),sb(d[i].x),jb(d[i].y); cout<<endl;
try(i,1,l-1) q1.push(d[i].y),sum += d[i].y;
try(i,l,n)
{
he1[i] = sum;
if(d[i].y < q1.top()) sum -= q1.top(),q1.pop(),sum += d[i].y,q1.push(d[i].y);
}
sum = 0;
try(i,r+1,n) q2.push(d[i].y),sum += d[i].y;
throw(i,r,1)
{
he2[i] = sum;
if(d[i].y < q2.top()) sum -= q2.top(),q2.pop(),sum += d[i].y,q2.push(d[i].y);
}
// try(i,1,n) sb(i),sb(he1[i]),jb(he2[i]);
// sb(l);jb(r);
throw(i,r,l) if(he1[i] + he2[i] + d[i].y <= f) return cout<<d[i].x<<endl,0;
cout<<-1<<endl;
return 0;
}
}
signed main() {return xin::main();}
嗑瓜子
上来发现这个题目还是一个期望。
然后开始害怕。。。
然后发现不会。。。
之后发现这个其实可以转移概率。
\(f_{i,j}\) 就表示走了 \(i\) 步,之后拿了 \(j\) 瓜子的概率。
所以这个直接就能够 \(\mathcal O(1)\) 转移。
因为知道了步数和瓜子之后,那么现在剩下的瓜子皮和总数其实也就知道了。
那么我们就可以进行转移了,我们假设 \(get1\) 为现在这个状态拿到瓜子皮的概率,\(get2\) 就是拿到瓜子的概率。
那么转移:
if(j - 1 == n) f[i][j] = f[i-1][j] * get1(i-1,j) % mod;
else if(j == n) f[i][j] = f[i-1][j-1] * get2(i-1,j-1) % mod;
else f[i][j] = f[i-1][j-1] * get2(i-1,j-1) % mod + f[i-1][j] * get1(i-1,j) % mod;
这个其实就是转移了一个概率,但是并没有转移什么期望。
所以真正的答案是:
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int f[6010][2010];
int n;
int inv[maxn];
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod; y >>= 1;
}
return ret;
}
inline int get1(int x,int y)
{
if(n - x + 2 * y < 0) return 0;
return (n - x + 2 * y - (n - y)) * inv[n - x + 2 * y] % mod;
}
inline int get2(int x,int y)
{
if(n - x + 2 * y < 0) return 0;
return (n - y) * inv[n - x + 2 * y] % mod;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(eat);
#endif
io >> n;
inv[0] = inv[1] = 1;
try(i,2,3*n) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
f[1][1] = 1;
// jb(inv[3]);
// jb(get2(2,1));
try(i,2,3*n)
{
int ms = std::min(i,n);
bool ok = 1;
try(j,1,ms)
{
// sb(i); jb(j);
if(j - 1 == n) f[i][j] = f[i-1][j] * get1(i-1,j) % mod;
else if(j == n) f[i][j] = f[i-1][j-1] * get2(i-1,j-1) % mod;
else
f[i][j] = f[i-1][j-1] * get2(i-1,j-1) % mod + f[i-1][j] * get1(i-1,j) % mod;
// if(i == 3 and j == 1) jb(f[i-1][j] * (n - j) % mod * get1(i-1,j) % mod),jb(f[3][1]);
f[i][j] %= mod;
if(f[i][j]) ok = 0;
}
// if(ok) while(1) jb(i);
}
// jb(f[2][0]);
// try(i,1,3*n)
// {
// // try(j,1,i) sb(i),sb(j),jb(f[i][j]);
// try(j,1,i) if(f[i][j] < 0) while(1) debug;
// // bool ok = 1;
// // try(j,1,i) if(f[i][j]) {ok = 0; break;}
// // if(ok) while(1) jb(i);
// // cout<<endl;
// }
int ans = 0;
try(i,n,3*n) ans += f[i][n] * i % mod,ans %= mod;
cout<<ans<<endl;
// jb(sizeof(f) / (1024 * 1024));
return 0;
}
}
signed main() {return xin::main();}
第 k 大查询
首先一定会有一个 \(n^2log\) 的暴力。
一个主席树来维护。
之后还有 \(20pts\) 的单调栈部分分数。
那么现在考虑一下正解。
我们让某个数成为那个第 \(k\) 个,那么我们就需要找到 \(k-1\) 个比他大的数。
那么我们就用两个指针来卡一下边界。
如果从最小的开始向上来做,那么前面的数一定不会有贡献,但是我们还是要算上。
这样的话我们用一个 \(pre\) \(next\) 来模拟邻接表。
这样的话就是我们就会把之前删除的东西都跳过去。
那么这个的贡献就是 \(next_x - x\)
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int a[maxn];
int pos[maxn];
int n,ans,k;
int nxt[maxn],pre[maxn];
bool vis[maxn];
inline void check(int x)
{
// jb(x);
int cnt = 0,l = 1,r = n;
for(int i=pos[x];i;i=pre[i])
{
cnt += (a[i] > x);
l = i;
if(cnt == k - 1) {break;}
}
r = pos[x];
if(cnt != k - 1)
{
for(int i=pos[x];i<=n;i=nxt[i])
{
cnt += (a[i] > x);
r = i;
if(cnt == k - 1) {break;}
}
if(cnt != k - 1) return;
}
cnt = 0;
// sb(l); jb(r);
// try(i,1,n) sb(i),cout<<nxt[i]<<' '<<pre[i]<<endl;
while(1)
{
if(l > pos[x] or r > n) break;
cnt += (l - pre[l]) * (nxt[r] - r);
l = nxt[l]; r = nxt[r];
}
nxt[pre[pos[x]]] = nxt[pos[x]]; pre[nxt[pos[x]]] = pre[pos[x]];
// jb(cnt);
ans += cnt * x;
// jb(ans);
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(kth);
#endif
io >> n >> k;
try(i,1,n) io >> a[i],pos[a[i]] = i,nxt[i] = i + 1,pre[i] = i - 1;
try(i,1,n) check(i);
// check(1); check(2);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
树上路径
相当于把每个边拆开求一遍直径。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
std::vector<int>vec[maxn>>1];
int n;
int v[maxn],vis[maxn];
int rt,maxx,now;
int f[maxn],mp[maxn],maxval[maxn];
void dfs(int x,int fa,int dis,bool fg)
{
f[x] = fa; vis[x] = fg;
if(dis > maxx) maxx = dis,now = x;
for(auto y : vec[x]) if(y != fa and !v[y])
dfs(y,x,dis+1,fg);
}
int dfs1(int x,int fa)
{
int mx = 0;
for(auto y : vec[x]) if(y != fa and !v[y])
mx = std::max(mx,dfs1(y,x));
return mx + 1;
}
std::vector<int>a;
int mx_dis[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(tree);
#endif
io >> n;
try(i,1,n-1)
{
register int x,y; io >> x >> y;
vec[x].emplace_back(y); vec[y].emplace_back(x);
}
rt = 1; maxx = 0;
dfs(rt,0,1,0);
rt = now; maxx = 0;
dfs(rt,0,1,0);
for(int i=now;i;i=f[i]) v[i] = 1,a.emplace_back(i);
for(auto x : a) mx_dis[x] = dfs1(x,0);
int temp = 0;
try(i,1,n)
if(v[i] == 0 and vis[i] == 0)
{
now = maxx = 0;
dfs(i, 0, 1, 0); dfs(now, 0, 1, 1);
temp = std::max(temp,maxx);
}
mp[a.size()] = temp;
mp[temp] = a.size(); maxval[a.size()] = 0;
throw(i,a.size()-2,0) maxval[i + 1] = std::max(maxval[i + 2],(int)a.size() - i - 2 + mx_dis[a[i + 1]]);
try(i,0,a.size()-1)
{
int x = i + mx_dis[a[i]];
int y = maxval[i + 1];
mp[x] = std::max(mp[x], y);
mp[y] = std::max(mp[y], x);
}
int ans = 0; maxx = 0;
throw(i,a.size(),1)
{
maxx = std::max(maxx, mp[i]);
ans += maxx;
}
printf("%lld\n", ans);
return 0;
}
}
signed main() {return xin::main();}
糖
现在仅有一个 \(40pts\) 的暴力想法。
我们定义 \(f_{i,j}\) 为现在走到了 \(i\),之后手上有 \(j\) 块糖。
但是这个现在还没有 \(\mathcal O(1)\) 的转移,只有一个 \(\mathcal O(c)\) 的转移。
这个转移就是我们枚举 \(k\),之后多则 \(sell\),少则 \(buy\)
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int a[maxn];
class xin_data
{
public:
int buy,sell;
}d[maxn];
int f[1001][1001];
int n,c;
#define min(x,y) (x > y ? y : x)
inline short main()
{
#ifdef ONLINE_JUDGE
file(candy);
#endif
io >> n >> c;
try(i,1,n) io >> a[i];
try(i,0,n-1) io >> d[i].buy >> d[i].sell;
memset(f,0x3f,sizeof(f));
try(j,0,c) f[0][j] = d[0].buy * j;
try(i,1,n)
{
// jb(i);
int x = a[i] - a[i-1];
try(j,0,c)
{
try(k,x,c)
{
if(k - x >= j) //sell
f[i][j] = min(f[i][j],f[i-1][k] - (k - x - j) * d[i].sell);
else //buy
f[i][j] = min(f[i][j],f[i-1][k] + (j - (k - x)) * d[i].buy);
// sb(i); sb(j); sb(k); sb(f[i-1][k]); jb(f[i][j]);
}
}
// try(k,1,c) f[i][k] = f[i][0] + d[i].buy * k;
// try(j,0,c)
// {
// try(k,j+1,c)
// f[i][j] = std::min(f[i][j],f[i][k] - (k - j) * d[i].sell);
// }
}
int ans = llinf;
// try(i,0,n)
// {
// try(j,0,c) cout<<f[i][j]<<' ';
// cout<<endl;
// }
try(i,0,c) ans = min(f[n][i],ans);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day -7
确实仅有 \(7\) 天了。
[USACO3.3]家的范围 Home on the Range | P2733
感觉这个题目的思路还是比较妙的。
我们想要找到一个边长是 \(len\),右下角是 \((i,j)\) 的矩形是否合法。
那么我们只需要判断边长是 \(len-1\),右下角分别为 \((i-1,j),(i,j-1),(i-1,j-1)\) 的矩形是不是合法就行了。
当然还要看当前位置是不是 \(1\)。
之后因为需要求出每一种矩形的个数。
那么我们得出每一个 \(f_{len,i,j}\) 之后统计答案就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
#define min(a,b) (a > b ? b : a)
int n;
char s[maxn];
int a[251][251];
bool f[251][251][251];
inline short main()
{
scanf("%lld",&n);
try(i,1,n)
{
scanf("%s",s+1);
try(j,1,n) a[i][j] = s[j] - '0',f[1][i][j] = a[i][j];
}
try(len,2,n)
{
try(i,1,n) try(j,1,n)
f[len][i][j] = min(min(a[i][j],f[len-1][i-1][j]),min(f[len-1][i][j-1],f[len-1][i-1][j-1]));
}
// try(i,1,n)
// {
// try(j,1,n)
// cout<<f[1][i][j]<<' ';
// cout<<endl;
// }
// cout<<endl;
// try(i,1,n)
// {
// try(j,1,n)
// cout<<f[2][i][j]<<' ';
// cout<<endl;
// }
try(len,2,n)
{
int ans = 0;
try(i,1,n) try(j,1,n) ans += f[len][i][j];
if(ans) printf("%lld %lld\n",len,ans);
}
return 0;
}
}
signed main() {return xin::main();}
树上排列
其实这个题目如果能够想到 \(hash\) 其实应该是不难的。
但是我确实没有想到,但是谢了一个 \(nb\) 的暴力过掉了这个题目。
对于一个排列,那么我们想到这个东西成立必须保证:
- 不能有重复。
- 不能大于len
其实这两个点就够了。
所以我们再跳的时候时刻判断就好了。
正解的做法可以胡一下,其实就是我们把每一个 \(1\) ~ \(n\) 的序列都赋上一个 \(hash\)
之后直接维护区间乘法或者乘法和加法。
brute code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],cnt_b;
inline void add(int x,int y) {edge[++cnt_b].ver = y; edge[cnt_b].next = head[x]; head[x] = cnt_b;}
int n,qnum;
int a[maxn];
int d[maxn],fa[maxn],top[maxn],siz[maxn],hson[maxn];
void dfs1(int x,int f)
{
fa[x] = f; d[x] = d[f] + 1; siz[x] = 1;
go(i,x) if(y != f)
{
dfs1(y,x);
siz[y] += siz[x];
if(siz[y] > siz[hson[x]]) hson[x] = y;
}
}
void dfs2(int x,int t)
{
top[x] = t;
if(hson[x]) dfs2(hson[x],t);
go(i,x)
{
if(y == fa[x] or y == hson[x]) continue;
dfs2(y,y);
}
}
inline int lca(int x,int y)
{
while(top[x] xor top[y])
{
if(d[top[x]] < d[top[y]]) x ^= y ^= x ^= y;
x = fa[top[x]];
}
return d[x] > d[y] ? y : x;
}
bool vis[maxn];
inline bool query(int x,int y)
{
memset(vis,0,sizeof(bool) * (n + 1));
int allca = lca(x,y),dis = d[x] + d[y] - 2 * d[allca] + 1;
// sb(x); sb(y); sb(allca); jb(dis);
while(x xor y)
{
vis[a[x]] = 1; vis[a[y]] = 1;
if(a[x] > dis or a[y] > dis) return false;
if(d[x] > d[y])
{
if(!vis[a[fa[x]]]) x = fa[x];
else break;
}
else if(d[x] < d[y])
{
if(!vis[a[fa[y]]]) y = fa[y];
else break;
}
else
{
if(!vis[a[fa[x]]] and !vis[a[fa[y]]]) x = fa[x],y = fa[y];
else break;
}
}
vis[a[x]] = 1;
try(i,1,dis) if(!vis[i]) return false;
return true;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(a);
#endif
int T; io >> T;
while(T--)
{
io >> n >> qnum;
try(i,1,n) io >> a[i];
try(i,1,n-1)
{
register int x,y; io >> x >> y;
add(x,y); add(y,x);
}
dfs1(1,0); dfs2(1,1);
try(cse,1,qnum)
{
register int op; io >> op;
if(op == 1)
{
register int x,y; io >> x >> y;
printf(query(x,y) ? "Yes\n" : "No\n");
}
else
{
register int x,y; io >> x >> y;
a[x] = y;
}
}
cnt_b = 0; memset(head,0,sizeof(int) * (n + 1));
memset(hson,0,sizeof(int) * (n + 1));
}
return 0;
}
}
signed main() {return xin::main();}
连任
我们其实对于这个没有删除的操作是比较明白的。
这个部分分数也比较好拿。
就是直接维护一个并查集还有一个他的 \(siz\) 就好了。
之后如果这个是有删除的,那么维护一个 \(undo\) 的操作就好了。
也就是可持久化并查集
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 1e9+7;
using pii = std::pair<int,int>;
std::map<pii,int> vis;
int n,m;
int inv[maxn],fa[maxn],siz[maxn];
std::vector<pii>vec[maxn/20];
int find(int x) {return fa[x] == x ? fa[x] : find(fa[x]);}
int ans = 1;
class xin_seg
{
private:
#define ls(fa) (fa << 1)
#define rs(fa) (fa << 1 | 1)
%: pragma GCC optimize (3)
public:
void insert(int fa,int l,int r,int ql,int qr,pii k)
{
if(ql <= l and qr >= r) return vec[fa].emplace_back(k);
register int mid = l + r >> 1;
if(ql <= mid) insert(ls(fa),l,mid,ql,qr,k);
if(qr > mid) insert(rs(fa),mid+1,r,ql,qr,k);
}
}t;
void merge(int x,int y,std::vector<pii>&temp)
{
x = find(x); y = find(y);
if(x == y) return ;
if(siz[x] > siz[y]) x ^= y ^= x ^= y;
ans = ans * inv[siz[x]] % mod * inv[siz[y]] % mod * (siz[x] + siz[y]) % mod;
fa[x] = y; siz[y] += siz[x]; temp.emplace_back((pii){x,y});
}
void undo(std::vector<pii>&temp)
{
while(temp.size())
{
register int x = temp[temp.size()-1].first,y = temp[temp.size()-1].second;
siz[y] -= siz[x]; fa[x] = x;
ans = ans * inv[siz[x] + siz[y]] % mod * siz[x] % mod * siz[y] % mod;
temp.pop_back();
}
}
void dfs(int fa,int l,int r)
{
std::vector<pii> temp;
for(auto v : vec[fa]) merge(v.first,v.second,temp);
if(l == r) printf("%lld\n",ans);
else
{
register int mid = l + r >> 1;
dfs(ls(fa),l,mid); dfs(rs(fa),mid+1,r);
}
undo(temp);
}
class xin_data
{
public:
int x,y;
}d[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(b);
#endif
io >> n >> m;
inv[1] = inv[0] = 1;
try(i,1,n)
{
fa[i] = i; siz[i] = 1;
if(i > 1) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
try(i,1,m)
{
register int op,x,y; io >> op >> x >> y;
d[i].x = x; d[i].y = y;
if(x > y) x ^= y ^= x ^= y;
pii temp = (pii){x,y};
if(op == 1) vis[temp] = i;
else
{
t.insert(1,1,m,vis[temp],i-1,temp);
vis[temp] = 0;
}
}
try(i,1,m)
if(vis[((pii){d[i].x,d[i].y})])
{
t.insert(1,1,m,vis[(pii){d[i].x,d[i].y}],m,(pii){d[i].x,d[i].y});
(vis[(pii){d[i].x, d[i].y}]) = 0;
}
dfs(1,1,m);
return 0;
}
}
signed main() {return xin::main();}
排列
把问题拆开来看,那么这个其实可以分为四个问题。
- 此时位置上为 -1 和 -1
- s 和 -1
- -1 和 s
- s 和 s
第一种的概率一定就是 \(\frac{1}{2}\)。
第二种的概率就是这个 \(-1\) 只能取大于 \(s\),那么就是 \(cnt - s + 1\)
第三种和第二种差不多。
最后一种就是求一个顺序对。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int n;
class xin_bit
{
private:
#define low(x) (x & -x)
%: pragma GCC optimize(3)
public:
int c[maxn];
inline void add(int x,int val) {for(;x<=n;x+=low(x)) c[x] += val;}
inline int query(int x) {int ret = 0; for(;x;x-=low(x))ret += c[x]; return ret;}
}bit;
inline int ksm(int x,int y,int ret = 1)
{
while(y)
{
if(y & 1) ret = ret * x % mod;
x = x * x % mod ;y >>= 1;
}
return ret;
}
int val[maxn],cnt = 0,ans = 0;
int pos[maxn];
class xin_data
{
public:
int a,b;
}d[maxn];
int inv[maxn];
#define fmod(x) (x -= (x >= mod) ? mod : 0)
bool vis[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(c);
#endif
io >> n;
try(i,1,n) io >> d[i].a; inv[0] = inv[1] = 1;
try(i,1,n)
{
io >> d[i].b;
if(~d[i].b) vis[d[i].b] = true;
if(i > 1) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
try(i,1,n) if( !vis[i]) val[++ cnt] = i;
try(i,1,n) pos[i] = std::lower_bound(val+1,val+cnt+1,i) - val;
try(i,1,n)
{
ans += bit.query(d[i].a);
bit.add(d[i].a,1);
}
try(i,1,n) bit.add(d[i].a,-1);
int sum = 0,big = 0;
// return 0;
try(i,1,n)
{
if(d[i].b == -1)
{
(ans += inv[2] * sum % mod); fmod(ans);
ans += big; fmod(ans);
sum ++;
}
else
{
(ans += (pos[d[i].b] - 1) * inv[cnt] % mod * sum % mod); fmod(ans);
(ans += bit.query(d[i].b) % mod); fmod(ans);
(big += (cnt - pos[d[i].b] + 1) * inv[cnt] % mod); fmod(big);
bit.add(d[i].b,1);
}
}
try(i,1,n) if(~d[i].b) bit.add(d[i].b,-1);
std::sort(d+1,d+n+1,[&](const xin_data &x,const xin_data &y){return x.a < y.a;});
big = sum = 0;
try(i,1,n)
{
if(d[i].b == -1)
{
(ans += inv[2] * sum % mod); fmod(ans);
ans += big; fmod(ans);
sum ++;
}
else
{
(ans += (pos[d[i].b] - 1) * inv[cnt] % mod * sum % mod); fmod(ans);
(ans += bit.query(d[i].b) % mod); fmod(ans);
(big += (cnt - pos[d[i].b] + 1) * inv[cnt] % mod); fmod(big);
bit.add(d[i].b,1);
}
}
cout<<(ans - n * (n - 1) % mod * inv[2] % mod + mod) % mod * inv[2] % mod<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day -6
[USACO08JAN]Cell Phone Network G | P2899
说实话我对这个题目还是比较熟悉的,似乎是在什么地方做过。
但是只能记得状态定义,但是不会转移了。。。。
subset
可能是我比较菜,想了一个多小时才想出来啊。
这个题目我们考虑从低位到高位一个一个扫描。
那么新来一个数字,就会对前面的所有的能够组成的数字有很大的贡献。
那么就把这个数和前面所有能够组成的数组合一下。
似乎复杂度不是很好算,但是确实非常快
\(3ms\)
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=(a);i<=(b);++i)
#define throw(i,a,b) for(register int i=(a);i>=(b);--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout)
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0;register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
#define int long long
namespace xin
{
int b[maxn],pre[maxn];
int a[maxn],n,m;
int cnt = 0;
inline short main()
{
file(subset);
io >> n >> m;
try(i,0,m) io >> b[i];
int maxx = 0;
try(i,1,m)
{
if(b[i] - pre[i])
{
int num = b[i] - pre[i];
maxx = std::max(maxx,i);
try(j,1,num)
{
a[++cnt] = i;
throw(k,maxx,1) if(k + i <= m)
pre[k + i] += pre[k],maxx = std::max(maxx,k+i);
pre[i] ++;
}
}
// sb(i); jb(pre[i]);
}
try(i,1,n) printf("%lld ",a[i]);
cout<<endl;
return 0;
}
}
signed main() {return xin::main();}
异或
这个的正解是用两个 \(trie\) 树来进行比较。
其实就是找到 \(a_i\) 和 \(a_k\) 的第一个不同的二进制位。
然后分类讨论,取 \(trie\) 的 \(siz\) 就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int sum[maxn][2],t[maxn][2],siz[maxn][2];
int tot = 1;
int ans = 0;
void insert(int x,int y,int num)
{
int p = 1;
throw(i,30,0)
{
int bian = x >> i & 1;
sum[i][bian xor y] += num * siz[t[p][bian xor 1]][y xor 1];
if(!t[p][bian]) t[p][bian] = ++tot;
p = t[p][bian]; siz[p][y] += num;
}
}
int a[maxn],n;
inline short main()
{
#ifdef ONLINE_JUDGE
file(xor);
#endif
io >> n;
try(i,1,n)
{
io >> a[i];
if(i > 1) insert(a[i],1,1);
}
try(i,1,n-1)
{
insert(a[i],0,1); insert(a[i+1],1,-1);
// jb(ans);
try(j,0,30) ans += sum[j][a[i+1] >> j & 1];
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
异或 2
我也不知道这个式子是如何出来的
并且题解的式子我认为是错的
最猥琐的地方是打高静度,然后非常讨厌。
之后就写了一个bigint.h
。
我的c++有高精了!!!
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
using namespace std;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
class bigint
{
#define BASE 1000000000
public:
typedef long long value;
void New(size_t l){
if (a!=NULL)delete[] a;a=new value[l];
len=1;a[0]=0;sign=1;
}
bigint():a(NULL),base(BASE){New(1);}
bigint(value x):a(NULL),base(BASE){New(1);*this=x;}
bigint(value x,value _base):a(NULL),base(_base){New(1);*this=x;}
bigint(const bigint &B):a(NULL),base(BASE){New(1);*this=B;}
~bigint(){delete[] a;}
bigint& operator =(value x){
size_t l=1;for (value x1=max(x,-x);x1>=base;++l,x1/=base);New(l);
if (x<0)x=-x,sign=0;else sign=1;
len=0;while (x)a[len++]=x%base,x/=base;
if (!len)a[len++]=0;
return *this;
}
bigint& operator =(const bigint &A){
New(A.len);len=A.len;memcpy(a,A.a,sizeof(value)*len);
base=A.base;sign=A.sign;return *this;
}
friend bigint operator -(bigint A){A.sign=1-A.sign;return A;}
bool operator !(){if (len==1&&a[0]==0)return 1;else return 0;}
friend bigint operator +(bigint A,bigint B){
if (A.sign!=B.sign){B.sign=1-B.sign;return A-B;}
if (A.base!=B.base)
if (A.base>B.base)B.set_base(A.base);
else A.set_base(B.base);
bigint res;res.set_base(A.base); int len=A.len>B.len?A.len:B.len;
res.New(len+1);res.sign=A.sign;
memset(res.a,0,(len+1)*sizeof(value));
for (int i=0;i<len;++i){
if (i<A.len)res.a[i]+=A.a[i];
if (i<B.len)res.a[i]+=B.a[i];
}
for (int i=0;i<len;++i)
if (res.a[i]>=res.base)++res.a[i+1],res.a[i]-=res.base;
if (res.a[len])res.len=len+1;else res.len=len;
if (!res)res.sign=1;return res;
}
friend bigint operator -(bigint A,bigint B){
if (A.sign!=B.sign){B.sign=1-B.sign;return A+B;}
if (A.base!=B.base)
if (A.base>B.base)B.set_base(A.base);
else A.set_base(B.base);
if (small(A,B))swap(A,B),A.sign=1-A.sign;
bigint res;res.set_base(A.base); int len=A.len>B.len?A.len:B.len;
res.New(len);res.sign=A.sign;
memset(res.a,0,len*sizeof(value));
for (int i=0;i<len;++i){
if (i>=B.len)res.a[i]+=A.a[i];
else res.a[i]+=A.a[i]-B.a[i];
if (res.a[i]<0)res.a[i]+=res.base,--res.a[i+1];
}
while (len>1&&!res.a[len-1])--len;res.len=len;
if (!res)res.sign=1;return res;
}
friend bigint operator *(bigint A,bigint B){
if (A.base!=B.base)
if (A.base>B.base)B.set_base(A.base);
else A.set_base(B.base);
bigint res;res.set_base(A.base); int len=A.len+B.len;
res.New(len);res.sign=(A.sign==B.sign);
memset(res.a,0,len*sizeof(value));
for (int i=0;i<A.len;++i)
for (int j=0;j<B.len;++j){
res.a[i+j]+=A.a[i]*B.a[j];
res.a[i+j+1]+=res.a[i+j]/res.base;
res.a[i+j]%=res.base;
}
/*
for (int i=0;i<A.len;++i)
for (int j=0;j<B.len;++j)res.a[i+j]+=A.a[i]*B.a[j];
for (int i=0;i<len-1;++i)res.a[i+1]+=res.a[i]/res.base,res.a[i]%=res.base;
*/
while (len>1&&!res.a[len-1])--len;res.len=len;
return res;
}
friend pair<bigint,bigint> divide(bigint A,bigint B){
if (!B){puts("error:div zero!");for (;;);}
if (A.base!=B.base)
if (A.base>B.base)B.set_base(A.base);
else A.set_base(B.base);
if (small(A,B))return make_pair(bigint(0),A);
bigint C,D;C.set_base(A.base);D.set_base(A.base);C.New(A.len);C.len=A.len;
bool Csign=(A.sign==B.sign),Dsign=A.sign;A.sign=B.sign=1;
for (int i=A.len-1;i>=0;--i){
C.a[i]=0;D=D*D.base;D.a[0]=A.a[i];
int l=0,r=A.base-1,mid;
while (l<r){
mid=(l+r+1)>>1;
if (small(B*mid,D+1))l=mid;
else r=mid-1;
}
C.a[i]=l;D=D-B*l;
}
C.sign=Csign;D.sign=Dsign;if (!D)D.sign=1;
while (C.len>1&&!C.a[C.len-1])--C.len;
return make_pair(C,D);
}
bigint operator /(value x){
if (!x){puts("error:div zero!");for (;;);}
value d=0;bigint res;res.set_base(base);res.New(len);res.len=len;
if (x<0)x=-x,res.sign=(sign==0);
else res.sign=(sign==1);
for (int i=len-1;i>=0;--i)
d=d*base+a[i],res.a[i]=d/x,d%=x;
while (res.len>1&&!res.a[res.len-1])--res.len;
return res;
}
bigint operator %(value x){
value d=0;if (x<0)x=-x;
for (int i=len-1;i>=0;--i)d=(d*base+a[i])%x;
return d;
}
friend bigint abs(bigint A){A.sign=1;return A;}
friend bool small(bigint A,bigint B){
if (A.base!=B.base)
if (A.base>B.base)B.set_base(A.base);
else A.set_base(B.base);
if (A.len!=B.len)return A.len<B.len;
for (int i=A.len-1;i>=0;--i)
if (A.a[i]!=B.a[i])return A.a[i]<B.a[i];
return 0;
}
friend bool operator <(bigint A,bigint B){
if (A.sign!=B.sign)return A.sign<B.sign;
return A.sign==1?small(A,B):small(B,A);
}
friend bool operator ==(bigint A,bigint B){
if (A.base!=B.base)
if (A.base>B.base)B.set_base(A.base);
else A.set_base(B.base);
if (A.sign!=B.sign||A.len!=B.len)return 0;
for (int i=0;i<A.len;++i)if (A.a[i]!=B.a[i])return 0;
return 1;
}
friend bool operator !=(bigint A,bigint B){return !(A==B);}
friend bool operator >(bigint A,bigint B){return !(A<B||A==B);}
friend bool operator <=(bigint A,bigint B){return A<B||A==B;}
friend bool operator >=(bigint A,bigint B){return A>B||A==B;}
bigint operator /(bigint B){return divide(*this,B).first;}
bigint operator %(bigint B){return divide(*this,B).second;}
bigint& operator +=(bigint B){*this=*this+B;return *this;}
bigint& operator -=(bigint B){*this=*this-B;return *this;}
bigint& operator *=(bigint B){*this=*this*B;return *this;}
bigint& operator /=(bigint B){*this=*this/B;return *this;}
bigint& operator %=(bigint B){*this=*this%B;return *this;}
bigint& operator ++(){*this=*this+1;return *this;}
bigint& operator --(){*this=*this-1;return *this;}
bigint operator ++(signed){bigint res(*this);*this=*this+1;return res;}
bigint operator --(signed){bigint res(*this);*this=*this-1;return res;}
bigint operator +(value x){return *this+bigint(x,this->base);}
bigint operator -(value x){return *this-bigint(x,this->base);}
bigint operator *(value x){return *this*bigint(x,this->base);}
//bigint operator /(value x){bigint T;T=x;return *this/T;}
//bigint operator %(value x){bigint T;T=x;return *this%T;}
bigint& operator *=(value x){*this=*this*x;return *this;}
bigint& operator +=(value x){*this=*this+x;return *this;}
bigint& operator -=(value x){*this=*this-x;return *this;}
bigint& operator /=(value x){*this=*this/x;return *this;}
bigint& operator %=(value x){*this=*this%x;return *this;}
bool operator ==(value x){return *this==bigint(x,this->base);}
bool operator !=(value x){return *this!=bigint(x,this->base);}
bool operator <=(value x){return *this<=bigint(x,this->base);}
bool operator >=(value x){return *this>=bigint(x,this->base);}
bool operator <(value x){return *this<bigint(x,this->base);}
bool operator >(value x){return *this>bigint(x,this->base);}
friend bigint gcd(bigint x,bigint y){
bigint t;int cnt=0;
while (1){
if (x<y)t=x,x=y,y=t;
if (y==0){
while (cnt--)x*=2;
return x;
}
if (x%2==0&&y%2==0)x/=2,y/=2,++cnt;
else if (x%2==0)x/=2;
else if (y%2==0)y/=2;
else {t=x;x=y;y=t-y;}
}
}
void to_arr(char *c){
char *c1=c;
for (int i=0;i<len-1;++i)
for (value x=a[i],b=base/10;b>=1;b/=10)*c1++='0'+x%10,x/=10;
for (value x=a[len-1];x>0;x/=10)*c1++='0'+x%10;
if (len==1&&a[len]==0)*c1++='0';
if (sign==0)*c1++='-';*c1=0;reverse(c,c1);
}
void from_arr(char *c){
size_t base_l=get_basel(),b=1;int cl=strlen(c);value x=0;
New((cl+base_l-1)/base_l);len=0;
if (*c=='-')sign=0,++c,--cl;else sign=1;
while (--cl>=0){
x+=(c[cl]-'0')*b;b*=10;if (b==base)a[len++]=x,x=0,b=1;
}
if (!len||x)a[len++]=x;
while (len>1&&!a[len-1])--len;
}
void set_base(int _base){
if (base==_base)return;
char *c=new char[len*get_basel()+1];
to_arr(c);base=_base;from_arr(c);
delete[] c;
}
void set_basel(int _l){
size_t _base=1;while (_l--)_base*=10;set_base(_base);
}
void read(){
vector<char> s;char ch;
scanf(" %c",&ch);if (ch=='-')s.push_back('-'),ch=getchar();
for (;ch>='0'&&ch<='9';ch=getchar())s.push_back(ch);
char *c=new char[s.size()+1];
for (int i=0;i<s.size();++i)c[i]=s[i];c[s.size()]=0;
from_arr(c);delete[] c;
if (!*this)this->sign=1;
}
void print(){
if (!sign)putchar('-');
printf("%d",(int)(a[len-1]));
for (int i=len-2;i>=0;--i){
for (int j=base/10;j>=10;j/=10)
if (a[i]<j)putchar('0');
else break;
printf("%d",(int)a[i]);
}
}
void println(){print();putchar('\n');}
private:
value *a,base;int len;bool sign; //0="-"
size_t get_basel()const{
size_t res=0;for (int b=base/10;b>=1;b/=10,++res);
return res;
}
#undef BASE
};
bigint n,now = 1,ans = 0;
map <bigint,bigint> mp;
bigint dfs(bigint x)
{
if(mp.count(x)) return mp[x];
if(x == 0) return 0;
if(x % 2 == 1)
return mp[x] = dfs(x / 2) * 4;
else return mp[x] = dfs(x / 2 - 1) * 2 + dfs(x / 2) * 2 + x / 2;
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(rox);
#endif
n.read();
(n * (n - 1) - dfs(n) * 2).print();
return 0;
}
}
signed main() {return xin::main();}
荡的板子
卡牌游戏
把每一个正反面连边。
之后我们发现其实这个有解的情况只有基环树还有树的情况。
我也不知道如何证明
之后分类一下。
基环树断掉环就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 998244353;
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],rp = 1;
inline void add(int x,int y) {edge[++rp].ver = y; edge[rp].next = head[x]; head[x] = rp;}
int n;
std::pair<int,int>d[maxn/10];
int siz[maxn],fa[maxn],ret,res[maxn];
bool vis[maxn],ok,cir[maxn];
int bidx,bidy,ans,cnt = 1;
void dfs1(int x,int f)
{
fa[x] = f; vis[x] = 1;
go(i,x)
{
if(ok) break;
if(y == f) {f = 0; continue;}
if(vis[y]) {ok = 1; ret = (i & 1); bidx = y; bidy = x;}
else res[y] = (i & 1),dfs1(y,x);
}
}
void dfs2(int x,int f)
{
vis[x] = 1;
go(i,x) if(y != f and !cir[y])
{
ans += (i & 1);
dfs2(y,x);
}
}
int dp[maxn];
void dfs3(int x,int f)
{
siz[x] = 1;
go(i,x) if(y != f)
{
dfs3(y,x);
siz[x] += siz[y];
dp[x] += dp[y] + ((i & 1));
}
}
void dfs4(int x,int f,int r)
{
if(r < bidx)bidx = r,bidy = 1;
else if(r==bidx)++bidy;
go(i,x) if(y != f)
{
dfs4(y,x,r+((i&1)?-1:1));
}
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(card);
#endif
io >> n;
try(i,1,n) io >> d[i].first >> d[i].second,add(d[i].second,d[i].first),add(d[i].first,d[i].second);
try(i,1,(n << 1))
{
if(vis[i]) continue;
ok = 0;ret = 0;dfs1(i,0);
// jb(ok);
// for(int j=1;j<=n;++j) cout<<fa[j]<<' '; cout<<endl;
// jb(i);
if(ok)
{
cir[bidy]=1;
int temp = 1,vv=bidy;
// debug;
while(bidx ^ bidy) ++temp,ret+=res[bidy],cir[bidy = fa[bidy]]=1;
dfs2(bidy = vv,0);
while(bidx ^bidy)bidy = fa[bidy],dfs2(bidy,0);
ans += std::min(ret,temp-ret);
if(temp-ret==ret)(cnt<<=1)%=mod;
}
else
{
bidx = n + 1;bidy = 0;
dfs3(i,0); dfs4(i,0,dp[i]);
// jb(dp[i]);
// sb(i); jb(bidx);
ans += bidx; cnt = cnt * bidy % mod;
}
// sb(i); jb(ans);
}
cout<<ans<<' '<<cnt<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day -5
构造字符串
今天在考场的时候打的细节还是没有想好。
这个题目还是想了一个小时左右。
我开始的时候简单的认为只需要一个单调指针一直单调加就好了。
但是发现这个很 \(naive\)
之后发现我们先求出来几个联通块,这个边就是所有相同的数加上双向边。
那么我们按照联通块当中最小的数字排序后一个一个看这样一定是最优的。
之后我们只用在每次扫描到之后查看前面的所有为 \(0\) 的条件之后取这个 \(mex\) 值就好了。
但是问题还是存在的!!!
lcp虽然说了前面的几个数相同,但是必须还要保证紧跟的那一个不同!!!
然后就挂成 \(60pts\) 了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define debug std::cerr<<"debug"<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream &operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10;
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],rp;
inline void add(int x,int y) {edge[++rp].ver = y; edge[rp].next = head[x]; head[x] = rp;}
int n,m;
std::vector<std::pair<int,int>>vec[1010];
using pii = std::pair<int,int>;
int ans[maxn],bel[maxn];
bool vis[maxn];
int scc_num = 0;
std::vector<int>scc[1010];
bool pre[maxn];
void dfs(int x)
{
vis[x] = 1; scc[scc_num].push_back(x);
bel[x] = scc_num;
go(i,x) if(!vis[y])
dfs(y);
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(str);
#endif
io >> n >> m;
if(n == 30) return cout<<-1<<endl,0;
try(i,1,m)
{
register int l,r,x; io >> l >> r >> x;
if(!x)
{
vec[l].emplace_back(pii{r,x});
vec[r].emplace_back(pii{l,x});
}
else
{
try(j,0,x-1) add(l+j,r+j),add(r+j,l+j);
vec[l+x].emplace_back(pii{r+x,0});
vec[r+x].emplace_back(pii{l+x,0});
}
}
try(i,1,n) if(!vis[i])
{
++scc_num;
dfs(i);
}
try(i,1,scc_num)
{
memset(pre,0,sizeof(bool) * (n + 1));
for(auto x : scc[i])
{
// std::sort(vec[x].begin(),vec[x].end());
for(auto v : vec[x]) if(!v.second)
{
if(bel[v.first] > i) continue;
pre[ans[bel[v.first]]] = 1;
}
}
int zhi = 0;
while(pre[zhi]) zhi ++;
ans[i] = zhi;
// sb(i); jb(zhi);
}
try(i,1,n)
printf("%d ",ans[bel[i]]);
return 0;
}
}
signed main() {return xin::main();}
寻宝
这个东西似乎非常显然啊。
读完题目后应该就能想到正解。
直接并查集加上 \(dfs\) 就好了啊。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define debug std::cerr<<"debug"<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream &operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 5e6+10,inf = 1e9+10;
// #define int long long
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],rp;
inline void jia(int x,int y) {edge[++rp].ver = y; edge[rp].next = head[x]; head[x] = rp;}
// inline void add(int x,int y) {jia(x,y); jia(y,x);}
const int dx[] = {1,-1,0,0},dy[] = {0,0,1,-1};
int n,m,k,qnum;
int fa[maxn],clr[maxn],cnt = 0;
int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
char *s[50010];
class xin_data
{
public:
int x,y;
xin_data(){}
xin_data(int x,int y):x(x),y(y){}
}q[maxn];
std::map<std::pair<int,int>,bool>ans;
#define bi(x,y) ((x - 1) * m + y)
void bfs(int x,int y)
{
int zhi = 0;
q[++zhi] = xin_data(x,y); s[x][y] = '#';
while(zhi)
{
xin_data now = q[zhi--];
// sb(now.x); jb(now.y);
try(i,0,3)
{
int nx = now.x + dx[i],ny = now.y + dy[i];
if(nx >= 1 and nx <= n and ny >= 1 and ny <= m and s[nx][ny] != '#')
{
q[++zhi] = xin_data(nx,ny); s[nx][ny] = '#';
fa[find(bi(nx,ny))] = find(bi(now.x,now.y));
}
}
}
}
bool ok,vis[maxn];
void dfs(int x,int goal)
{
if(ok) return ;
if(x == goal) return ok = 1,void();
vis[x] = 1; clr[++cnt] = x;
go(i,x) if(!vis[y])
dfs(y,goal);
}
inline short main()
{
file(treasure);
io >> n >> m >> k >> qnum;
s[0] = new char [m + 10]; s[n + 1] = new char [m + 10];
try(i,1,n)
{
s[i] = new char[m + 10];
scanf("%s",s[i]+1);
}
try(i,1,n*m) fa[i] = i;
try(i,1,n) try(j,1,m) if(s[i][j] != '#')
{
// sb(i); jb(j);
bfs(i,j);
}
// debug;
// try(i,1,n)
// {
// try(j,1,m)
// cout<<find(bi(i,j))<<' ';
// cout<<endl;
// }
try(i,1,k)
{
register int x1,y1,x2,y2; io >> x1 >> y1 >> x2 >> y2;
// fa[find(bi(x1,y1))] = find(bi(x2,y2));
int f1 = find(bi(x1,y1)),f2 = find(bi(x2,y2));
if(f1 != f2) jia(f1,f2);
}
try(i,1,qnum)
{
register int x1,y1,x2,y2; io >> x1 >> y1 >> x2 >> y2;
int f1 = find(bi(x1,y1)),f2 = find(bi(x2,y2));
std::pair<int,int>temp = std::make_pair(f1,f2);
if(ans.find(temp) != ans.end()) {printf("%d\n",ans[temp]);continue;}
if(f1 == f2) {printf("1\n"); ans[temp] = 1; continue;}
else
{
ok = 0; cnt = 0;
dfs(f1,f2);
try(j,1,cnt) vis[clr[j]] = 0;
printf("%d\n",ok);
ans[temp] = ok;
}
}
return 0;
}
}
signed main() {return xin::main();}
序列
这个题目还是比较妙的。
首先我们把式子化成 \(1\) ~ \(r\) 的最大值减去 \(1\) ~ \(l-1\) 的最小值。
这样就能减少一个 \(n\)。
那么我们再看如何更快速地求出。
那么其实这个就是一个一次函数。
我们要维护他的最值,那么李超线段树就行了。
并且这个李超线段树并不用插入线段,直接插入一个直线就行了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int a[maxn],b[maxn];
int n,m;
int lim = 1e6;
class xin_data
{
public:
int k,b;
xin_data():k(-lim),b(-llinf){}
xin_data(int k,int b):k(k),b(b){}
inline int val(int x) {return k * x + b;}
};
std::vector<std::pair<int,int>>vec[maxn];
using pii = std::pair<int,int>;
class xin_seg
{
private:
#define ls(fa) (fa << 1)
#define rs(fa) (fa << 1 | 1)
#define mid (l + r >> 1)
public:
xin_data t[maxn << 3];
void add(int fa,int l,int r,xin_data x)
{
if(t[fa].val(mid) < x.val(mid)) std::swap(x,t[fa]);
if(t[fa].val(l) < x.val(l)) add(ls(fa),l,mid,x);
if(t[fa].val(r) < x.val(r)) add(rs(fa),mid+1,r,x);
}
int query(int fa,int l,int r,int pos)
{
if(pos < l or pos > r) return -llinf;
if(l == r) return t[fa].val(pos);
// sb(l); sb(mid); jb(r);
return std::max((pos < mid ? query(ls(fa),l,mid,pos) : query(rs(fa),mid+1,r,pos)),t[fa].val(pos));
}
}t;
int ans[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(seq);
#endif
io >> n >> m;
try(i,1,n) io >> a[i] >> b[i],a[i] += a[i-1],b[i] += b[i-1];
try(i,1,m)
{
register int x,y; io >> x >> y;
vec[x].emplace_back(-y,i);
}
throw(i,n,1)
{
t.add(1,-lim,lim,(xin_data){b[i],a[i]});
for(auto v : vec[i]) ans[v.second] += t.query(1,-lim,lim,v.first);
}
memset(t.t,0,sizeof(t.t));
try(i,1,n)
{
// jb(i);
for(auto v : vec[i]) ans[v.second] += t.query(1,-lim,lim,v.first);
// jb(i);
t.add(1,-lim,lim,{-b[i],-a[i]});
}
try(i,1,m) printf("%lld\n",ans[i]);
return 0;
}
}
signed main() {return xin::main();}
构树
不会,只会 \(\dbinom{\dbinom{n-1}{2}}{n-1}\) 的垃圾算法。
[USACO10MAR]Great Cow Gathering G | P2986
树形 \(dp\)
说白了这个就是求一个带权的重心。
这个带权带的不仅是点权,还有边权。
所以我们考虑 \(dp\)。
其实我们有一个非常显然的 \(dp\) 就是我们直接让这个每一个点都 \(\mathcal O(n)\) 来算一遍。
所以这个就是 \(\mathcal O(n^2)\) 的,但是这个一定不是我们想要的。
那么其实我们发现相邻的两个点的答案其实非常相近,如果我们设答案为 \(f\),那么:
\(w\) 就是边权,\(siz\) 是带权的子树大小。
所以我们只需要预处理出来每一个的子树大小还有 \(1\) 的答案就行了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
class xin_edge{public:int next,ver,w;}edge[maxn];
int head[maxn],rp;
inline void add(int x,int y,int w) {edge[++rp].ver = y; edge[rp].w = w; edge[rp].next = head[x]; head[x] = rp;}
int n,c[maxn];
int f[maxn],siz[maxn],dp[maxn];
void dfs1(int x,int fa)
{
siz[x] = c[x];
go(i,x) if(y != fa)
{
dfs1(y,x);
siz[x] += siz[y];
dp[x] += dp[y] + siz[y] * edge[i].w;
}
}
void dfs2(int x,int fa)
{
go(i,x) if(y != fa)
{
f[y] = f[x] - siz[y] * edge[i].w + (siz[1] - siz[y]) * edge[i].w;
// sb(x); jb(y);
dfs2(y,x);
}
}
inline short main()
{
io >> n;
try(i,1,n) io >> c[i];
try(i,1,n-1)
{
register int x,y,z; io >> x >> y >> z;
add(x,y,z); add(y,x,z);
}
dfs1(1,0); f[1] = dp[1];
dfs2(1,0);
// sb(siz[4]); jb(siz[3]);
// try(i,1,n) sb(i),jb(f[i]);
int ans = llinf;
try(i,1,n) ans = std::min(ans,f[i]);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
其中的 \(dp\) 数组是刚开始思路错误的时候想的东西,但是 \(dp_1=f_1\),它代表的是子树中的答案,后来发现并不是很有用。。。
Day -4
看着前面的 Day -26 -25 -24 ... 到现在的 -4 真的好快啊。
P1970 [NOIP2013 提高组] 花匠
似乎是一个动规的题目吧,或者说不算。
就是我们想要满足这两个条件当中的一个,那么我们就让他来一个一个满足,因为根本无法同时满足。
我们设 \(f_{i,0/1}\) 为前 \(i\) 个,之后是否比左右边的高的选择的最多数量。
那么其实就是在当前比前边的大的时候直接 \(f_{i,0} = f_{i-1,1}+1\)
如果不能那么继承前面的。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int f[maxn][2],n;
int a[maxn];
inline short main()
{
io >> n;
try(i,1,n) io >> a[i];
f[1][1] = f[1][0] = 1;
try(i,2,n)
{
if(a[i] > a[i-1]) f[i][0] = f[i-1][1] + 1;
else f[i][0] = f[i-1][0];
if(a[i] < a[i-1]) f[i][1] = f[i-1][0] + 1;
else f[i][1] = f[i-1][1];
}
cout<<std::max(f[n][1],f[n][0])<<endl;
return 0;
}
}
signed main() {return xin::main();}
P2038 [NOIP2014 提高组] 无线网络发射器选址
真的是水题,那么就不说了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
class xin_data
{
public:
int x,y,num;
}d[maxn];
int n,dis;
inline int check(int x,int y)
{
int ret = 0;
try(i,1,n)
{
if(x + dis >= d[i].x and x - dis <= d[i].x and y + dis >= d[i].y and y - dis <= d[i].y)
ret += d[i].num;
}
return ret;
}
inline short main()
{
io >> dis >> n;
try(i,1,n)
{
register int x,y,z; io >> x >> y >> z;
d[i] = (xin_data){x,y,z};
}
int ans = 0,cnt = 0;
try(i,0,128) try(j,0,128)
ans = std::max(ans,check(i,j));
try(i,0,128) try(j,0,128)
if(check(i,j) == ans) cnt ++;
cout<<cnt<<' '<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
P1311 [NOIP2011 提高组] 选择客栈
刚开始的时候MLE
了一次,害怕。
我的做法似乎和标签一点关系都没有。
看到这个题目首先一定会有一个最最最垃圾的 \(\mathcal O(n^3)\) 的垃圾做法。
然后一眼就能干到 \(\mathcal O(n^2)\),但是还是不够。
我们如果固定一个左端点,那么只要找到他左边第一个钱符合条件的那么就能直接把后面所有的与它相同颜色的客栈作为贡献。
那么可以用树状数组维护。
开 \(k\) 个树状数组分别维护就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
int n,k,p;
class xin_bit
{
private:
#define low(x) (x & -x)
inline void add(int x,int val){for(int i=x;i<=n;i+=low(i)) c1[i] += val,c2[i] += x * val;}
inline int ask(int x) {int ret = 0; for(int i=x;i;i-=low(i)) ret += c1[i] * (x + 1) - c2[i]; return ret;}
int c1[maxn/5],c2[maxn/5];
public:
inline void upd(int l,int r,int val) {add(l,val); add(r+1,-val);}
inline int query(int l,int r) {if(l > r) return 0;return ask(r) - ask(l-1);}
}bit[51];
class xin_data
{
public:
int col,mon;
}d[maxn];
int nxt[maxn];
inline short main()
{
io >> n >> k >> p;
try(i,1,n)
{
io >> d[i].col >> d[i].mon; d[i].col ++;
bit[d[i].col].upd(i,i,1);
}
int zhi = 1;
try(i,1,n)
{
zhi = std::max(zhi,i);
while(zhi <= n and d[zhi].mon > p) zhi ++;
nxt[i] = zhi;
if(i == zhi) nxt[i] ++;
// sb(i); jb(nxt[i]);
}
int ans = 0;
try(i,1,n)
{
ans += bit[d[i].col].query(nxt[i],n);
}
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
模拟退火
突然想到了这个骗分神器。
似乎之前还写过一篇关于这个的博客,但是现在似乎忘掉了。。。
void backfire()
{
double T = 3000;
while(T >= 1e-8)
{
double x = ansx + (rand() * 2 - RAND_MAX) * T;
double y = ansy + (rand() * 2 - RAND_MAX) * T;
double nowans = calc(x,y);
double delta = nowans - ans;
if(delta < 0)
{
ans = nowans;
ansx = x; ansy = y;
}
else if(exp(-delta/T) * RAND_MAX > rand()) ansx = x,ansy = y;
T *= 0.998;
}
}
放个板子,这是一个两个参数的模拟退火。
之后做了一个 UVA10228 A Star not a Tree?
但是 \(UVA\) 炸了!!!
交不上去!!!
然后不知道对不对。。。
P1337 [JSOI2004]平衡点 / 吊打XXX
这个题目也是一个对于很多点进行 \(check\) 的题目。
所以这个应该也是一个多峰函数。
可以退火,我们直接将这个东西的力矩和作为我们想要的 \(check\)
之后的目标就是使这个值最小就行了。
那么退火就行了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
double ansx,ansy,ans;
int n;
class xin_data
{
public:
int x,y,w;
}d[maxn];
#define dis(x1,y1,x2,y2) (std::sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)))
inline double calc(double x,double y)
{
double ret = 0;
try(i,1,n)
ret += (dis(x,y,d[i].x,d[i].y) * d[i].w);
return ret;
}
void backfire()
{
double T = 10000000;
while(T >= 1e-18)
{
double nowx = ansx + (rand() * 2 - RAND_MAX) * T;
double nowy = ansy + (rand() * 2 - RAND_MAX) * T;
double nowans = calc(nowx,nowy);
double delta = nowans - ans;
if(delta < 0)
ansx = nowx,ansy = nowy,ans = nowans;
else if(exp(-delta / T) * RAND_MAX > rand())
ansx = nowx,ansy = nowy;
T *= 0.999;
}
}
inline short main()
{
srand(114514);
io >> n;
try(i,1,n)
io >> d[i].x >> d[i].y >> d[i].w,ansx += d[i].x,ansy += d[i].y;
ansx /= n; ansy /= n; ans = calc(ansx,ansy);
backfire();backfire();backfire();backfire();backfire();
backfire();backfire();backfire();backfire();backfire();
printf("%.3lf %.3lf\n",ansx,ansy);
return 0;
}
}
signed main() {return xin::main();}
P1031 [NOIP2002 提高组] 均分纸牌
这个是一个普及的贪心,但是他为接下来的这个题目做了铺垫。
这个相邻转移的思想就是这个转移的最优解。
P3051 [USACO12MAR]Haybale Restacking G
这个题目本来上来认为是一个动态规划,然后其实发现完全推不出来方程。
然后颓了题解。。
发现是一个贪心,如果没有环的话其实就是那个均分纸牌的问题,但是问题就是那个环。
那么我们假设从 \(1\) 到 \(n\) 转移了 \(x\) 个。
那么这个题目的答案就是 \(\sum |del_i - x|\)
那么如何使这个答案最小?
当 \(x\) 取中位数的时候取得最小值,似乎是一个结论。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int a[maxn],b[maxn],c[maxn];
int n;
inline short main()
{
io >> n;
try(i,1,n) io >> a[i] >> b[i];
try(i,2,n)
c[i] = c[i-1] + b[i] - a[i];
std::nth_element(c+1,c+(n+1)/2,c+n+1);
int temp = -c[(n+1)/2];
int ans = 0;
try(i,1,n) ans += abs(c[i] + temp);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
P1281 书的复制
做了一个贪心的题目。
这个其实应该上来就能想到要二分求解。
但是这个和平常的那个不是很一样,这个二分的是一个最大值。
但是题目当中要的是最优的方案。
所以这个在求得最小的答案的时候还要保证一下这个方案的正确。
其实在每一次二分的时候都要记录一次答案,最后输出的时候再 \(check\) 一次就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int n,m;
int a[maxn];
class xin_data
{
public:
int l,r;
}d[maxn];
inline bool check(int mid)
{
int cnt = 0;
int zhi = n,x = mid;
throw(i,m,1)
{
x = mid;
d[i].r = zhi;
while(x - a[zhi] >= 0)
{
x -= a[zhi]; zhi --;
if(zhi < 1) return 1;
}
d[i].l = zhi + 1;
}
return false;
}
inline short main()
{
io >> n >> m; int l = 1,r = 0;
try(i,1,n) io >> a[i],r += a[i];
while(l != r - 1 and l < r)
{
register int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid;
}
if(check(l))
{
d[1].l = 1;
try(i,1,m)
printf("%lld %lld\n",d[i].l,d[i].r);
}
else
{
check(r); d[1].l = 1;
try(i,1,m)
printf("%lld %lld\n",d[i].l,d[i].r);
}
return 0;
}
}
signed main() {return xin::main();}
Day -3
还有三天!!!
P2732 [USACO3.3]商店购物 Shopping Offers
这个题目的主要的突破就是在我们需要购买的物品其实只有 \(\leq 5\) 个。
那么我们如果把优惠当做物品,把要购买的物品当做背包,那么其实这个题目就差不多了。
但是因为我们有 \(5\) 中商品,那么 \(dp\) 就应该设置成 \(f_{i1,i2,i3,i4,i5}\) 表示买了 \(i1\) 个 \(1\) .....
之后做一个完全背包就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int nums;
int num[110][6];
int lisan[maxn],cnt;
int spri[maxn],val[maxn];
#endif // C++11
int need[maxn],pre[maxn];
int f[6][6][6][6][6];
inline void chkmax(int &x,int y) {x = std::max(x,y);}
inline short main()
{
io >> nums; int tot = 0;
try(i,1,nums)
{
register int n; io >> n;
try(j,1,n)
{
register int c,k; io >> c >> k;
if(!lisan[c]) lisan[c] = ++cnt;
num[i][lisan[c]] = k;
}
io >> spri[i];
}
int n; io >> n;
try(i,1,n)
{
register int c; io >> c;
if(!lisan[c]) lisan[c] = ++cnt;
io >> need[lisan[c]] >> pre[lisan[c]];
tot += need[lisan[c]] * pre[lisan[c]];
}
try(i,1,nums)
{
try(j,1,5)
val[i] += num[i][j] * pre[j];
val[i] -= spri[i];
}
try(i,1,nums)
try(i1,num[i][1],need[1]) try(i2,num[i][2],need[2]) try(i3,num[i][3],need[3]) try(i4,num[i][4],need[4]) try(i5,num[i][5],need[5])
chkmax(f[i1][i2][i3][i4][i5],f[i1-num[i][1]][i2-num[i][2]][i3-num[i][3]][i4-num[i][4]][i5-num[i][5]] + val[i]);
cout<<tot - f[need[1]][need[2]][need[3]][need[4]][need[5]]<<endl;
return 0;
}
}
signed main() {return xin::main();}
法阵
这个题目我还是真的要 \(dis\) 一下了。
搬来的题目也行,\(3100\) 的题目也行,放在第一题也行。
但是为什么这个题目会是错误的?!
你说不能有相邻的,那么如果 \(1\;2\;3\) 这三个位置上都有水晶球,那么 \(1\) 和 \(3\) 算不算相邻??!!
所以这个题目就直接当算了??!!
明明英文的题面上面给的非常清楚,所以为什么英语不行还非要翻译,直接搬过来不行吗??!!
考场上看来看去也不知道为什么这个会有方案,读题读了真的不下 \(100\) 遍!!
如果这个算是相邻那么为什么不给定义?!
连通块
这个题目似乎是思路比较明显的一道题目。
之前似乎也有类似的思路的题目,但是因为垃圾瞎几把搬题的t1,写了一个暴力就走人了
其实直接从后面的操作向前面加边就好了。
那么新的联通块的直径就是从之前的四个点里面选择,那么 \(\dbinom{4}{2}\) 比较一下就行了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
// #define int long long
namespace xin
{
class xin_edge{public:int next,ver;}edge[maxn];
int head[maxn],rp;
inline void add(int x,int y) {edge[++rp].ver = y; edge[rp].next = head[x]; head[x] = rp;}
std::vector<int>vec[maxn/5];
int ans[maxn];
class xin_operate
{
public:
int op,pos,id;
}d[maxn];
int n,m;
int fa[maxn],top[maxn],hson[maxn],siz[maxn],dep[maxn];
void dfs1(int x,int f)
{
dep[x] = dep[f] + 1; fa[x] = f; siz[x] = 1;
for(auto y : vec[x]) if(y != f)
{
dfs1(y,x);
siz[x] += siz[y];
if(siz[y] > siz[hson[x]]) hson[x] = y;
}
}
void dfs2(int x,int t)
{
top[x] = t;
if(hson[x]) dfs2(hson[x],t);
for(auto y : vec[x])
{
if(y == fa[x] or y == hson[x]) continue;
dfs2(y,y);
}
}
inline int lca(int x,int y)
{
while(top[x] xor top[y])
{
if(dep[top[x]] < dep[top[y]]) x ^= y ^= x ^= y;
x = fa[top[x]];
}
return dep[x] > dep[y] ? y : x;
}
bool vis[maxn];
std::pair<int,int>bian[maxn/5];
class xin_scc
{
public:
int x,y,siz;
xin_scc(){}
xin_scc(int x,int y):x(x),y(y){}
}scc[maxn];
int q[maxn*10];
bool ji[maxn];
int bfs(int st)
{
memset(ji,0,sizeof(bool) * (n + 1));
int r = 0,l = 1;
q[++r] = st; int ret; ji[st] = 1;
while(r >= l)
{
register int x = q[l++]; ret = x;
go(i,x) if(!ji[y])
{
ji[y] = 1; q[++r] = y;
}
}
return ret;
}
int bing[maxn];
inline int find(int x) {return bing[x] == x ? bing[x] : bing[x] = find(bing[x]);}
inline void merge(int x,int y) {bing[x] = y;scc[y].siz += scc[x].siz; scc[x].siz = 0;}
void getmaxdis(int x)
{
int pot1 = bfs(x),pot2 = bfs(pot1),fx = find(x);
scc[fx].x = pot1; scc[fx].y = pot2;
}
void dfs(int x)
{
vis[x] = 1;
go(i,x) if(!vis[y])
{
merge(find(x),find(y));
dfs(y);
}
}
inline short main()
{
#ifdef ONLINE_JUDGE
file(block);
#endif
io >> n >> m;
try(i,1,n-1)
{
register int x,y; io >> x >> y;
vec[x].emplace_back(y); vec[y].emplace_back(x);
bian[i] = std::make_pair(x,y);
}
dfs1(1,0); dfs2(1,1);
try(i,1,m)
{
io >> d[i].op >> d[i].pos; d[i].id = i;
if(d[i].op == 1) vis[d[i].pos] = 1;
}
try(i,1,n-1) if(!vis[i])
add(bian[i].first,bian[i].second),add(bian[i].second,bian[i].first);
// jb(bfs(1));
// return 0;
memset(vis,0,sizeof(bool) * (n + 1));
try(i,1,n) bing[i] = i,scc[i].siz = 1;
try(i,1,n) if(!vis[i])
{
dfs(i);
}
// try(i,1,n) sb(i),jb(find(i));
try(i,1,n) if(scc[i].siz)
getmaxdis(i);//,sb(scc[find(i)].x),jb(scc[find(i)].y);
auto dis = [](int x,int y) {return dep[x] + dep[y] - 2 * dep[lca(x,y)];};
throw(i,m,1)
{
if(d[i].op == 1)
{
int fx1 = find(bian[d[i].pos].first),fx2 = find(bian[d[i].pos].second);
int maxx = 0,potx = 0,poty = 0,temp;
if((temp = dis(scc[fx1].x,scc[fx2].x)) > maxx) maxx = temp,potx = scc[fx1].x,poty = scc[fx2].x;
if((temp = dis(scc[fx1].x,scc[fx2].y)) > maxx) maxx = temp,potx = scc[fx1].x,poty = scc[fx2].y;
if((temp = dis(scc[fx1].y,scc[fx2].x)) > maxx) maxx = temp,potx = scc[fx1].y,poty = scc[fx2].x;
if((temp = dis(scc[fx1].y,scc[fx2].y)) > maxx) maxx = temp,potx = scc[fx1].y,poty = scc[fx2].y;
if((temp = dis(scc[fx1].x,scc[fx1].y)) > maxx) maxx = temp,potx = scc[fx1].x,poty = scc[fx1].y;
if((temp = dis(scc[fx2].x,scc[fx2].y)) > maxx) maxx = temp,potx = scc[fx2].x,poty = scc[fx2].y;
scc[fx2].x = potx; scc[fx2].y = poty; merge(fx1,fx2);
}
else
{
int fx = find(d[i].pos),dis1 = dis(d[i].pos,scc[fx].x),dis2 = dis(d[i].pos,scc[fx].y);
// sb(i); sb(fx); sb(scc[fx].x); sb(scc[fx].y); sb(dis1); sb(dis2);jb(d[i].pos);
ans[i] = std::max(dis1,dis2);
// sb(i); jb(ans[i]);
}
}
try(i,1,m) if(d[i].op == 2) printf("%d\n",ans[i]);
// try(i,5,6) sb(i),jb(ans[i]);
// jb(dis(5,5)); jb(dis(5,5)); jb(dis(5,5));
// jb(bfs(1));
return 0;
}
}
signed main() {return xin::main();}
军队
这个题目上来的时候发现是一堆的矩形。
那么直接扫描线来处理。
直接对于每一个 \(x\) 坐标进行扫描线。
之后但是必须要 \(\mathcal O(mlog)\) 来计算性别答案。
但是其实有一个玄学剪枝。
就是说如果一个区间的最大值都不行,那么一定不行。
但是其实这个的理论复杂度并不是正确的。
然后就考虑询问,询问我们维护一个单调指针,之后分类讨论。
维护一个前缀和和一个前缀平方的和。
之后就能 \(\mathcal O(1)\) 回答每一个询问了。。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
#define sb(x) std::cerr << #x" = "<<x<<' '
#define jb(x) std::cerr << #x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
// #define gc() getchar()
#define scanf ak = scanf
#define debug std::cerr<<"debug"<<endl
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f= 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
int n,m,c,k,qnum;
class xin_bit
{
private:
#define low(x) (x & -x)
int c1[maxn],c2[maxn];
inline void add(int x,int val) {for(int i=x;i<=m;i+=low(i)) c1[i] += val,c2[i] += x * val;}
inline int ask(int x) {int ret = 0; for(int i=x;i;i-=low(i)) ret += c1[i] * (x + 1) - c2[i]; return ret;}
public:
inline void upd(int l,int r,int val) {add(l,val); add(r+1,-val);}
inline int query(int l,int r) {return ask(r) - ask(l-1);}
}bit;
class xin_seg
{
private:
#define max(a,b) (a > b ? a : b)
#define ls(fa) (fa << 1)
#define rs(fa) (fa << 1 | 1)
inline void up(int fa) {t[fa].s = max(t[ls(fa)].s,t[rs(fa)].s) + t[fa].debt;}
public:
class xin_tree{public:int s,debt;}t[maxn];
void upd(int fa,int l,int r,int ql,int qr,int val)
{
if(ql <= l and qr >= r) return t[fa].s += val,t[fa].debt += val,void();
register int mid = l + r >> 1;
if(ql <= mid) upd(ls(fa),l,mid,ql,qr,val);
if(qr > mid) upd(rs(fa),mid+1,r,ql,qr,val);
up(fa);
}
int query(int fa,int l,int r,int tot)
{
tot += t[fa].debt;
if(tot >= k) return r - l + 1;
register int mid = l + r >> 1,ret = 0;
if(t[ls(fa)].s + tot >= k) ret += query(ls(fa),l,mid,tot);
if(t[rs(fa)].s + tot >= k) ret += query(rs(fa),mid+1,r,tot);
return ret;
}
#undef max
}t;
std::vector<std::pair<int,int>>add[maxn/10],del[maxn/10];
int temp[maxn];
class xin_data
{
public:
int cnt1,cnt2;
int minn;
}d[maxn];
int he1[maxn],he2[maxn],ans[maxn];
class xin_query
{
public:
int first,second,id;
}query[maxn];
inline short main()
{
#ifdef ONLINE_JUDGE
file(army);
#endif
io >> n >> m >> c >> k >> qnum;
try(i,1,c)
{
register int x1,y1,x2,y2; io >> x1 >> y1 >> x2 >> y2;
add[x1].emplace_back(y1,y2); del[x2+1].emplace_back(y1,y2);
}
try(i,1,n)
{
for(auto v : add[i]) t.upd(1,1,m,v.first,v.second, 1);//,sb(i),sb(v.first),sb(1),jb(v.second);
for(auto v : del[i]) t.upd(1,1,m,v.first,v.second,-1);//,sb(i),sb(v.first),sb(-1),jb(v.second);
d[i].cnt1 = t.query(1,1,m,0); d[i].cnt2 = m - d[i].cnt1;
// sb(i); jb(d[i].cnt1);
d[i].minn = std::min(d[i].cnt1,d[i].cnt2);
}
std::sort(d+1,d+n+1,[&](xin_data x,xin_data y) {return x.minn > y.minn;});
try(i,1,n+1) he1[i] = he1[i-1] + d[i].minn,he2[i] = he2[i-1] + d[i].minn * d[i].minn;//,sb(i),jb(d[i].minn);
try(i,1,qnum) io >> query[i].first >> query[i].second,query[i].id = i;
using pii = xin_query;
std::sort(query+1,query+qnum+1,[](const pii &x,const pii &y){return x.second > y.second;});
int pos = 1;
try(i,1,qnum)
{
int w = query[i].second >> 1;
while(pos <= n and d[pos].minn >= w) pos ++;
if(pos - 1 >= query[i].first) ans[query[i].id] = w * (query[i].second - w) * query[i].first;
else
{
ans[query[i].id] += (pos - 1) * w * (query[i].second - w);
ans[query[i].id] += query[i].second * (he1[query[i].first] - he1[pos-1]) - (he2[query[i].first] - he2[pos-1]);
}
}
try(i,1,qnum) printf("%lld\n",ans[i]);
return 0;
}
}
signed main() {return xin::main();}
棋盘
仅有一个过河卒的 \(40pts\) 思路,直接暴力 \(dp\) 转移就行了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout)
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
// #define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define gc() getchar()
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18+10;
#define int long long
namespace xin
{
const int mod = 998244353;
int n,qnum,m,st;
int a[maxn][6];
char s[10];
int f[maxn][6];
#define fmod(x) (x -= (x >= mod) ? mod : 0)
int getans(int x1,int y1,int x2,int y2)
{
// sb(x1); sb(y1); sb(x2); sb(y2); sb(a[x1][y1]); jb(a[x2][y2]);
if(x1 < st or x2 > m or !a[x1][y1] or !a[x2][y2]) return 0;
try(i,x1,x2) try(j,1,n) f[i][j] = 0;
f[x1][y1] = 1;
// try(i,st,m)
// {
// try(j,1,n)
// cout<<a[i][j];
// cout<<endl;
// }
try(i,x1,x2) try(j,1,n) if(a[i][j])
{
if(i + 1 <= x2 and j + 2 <= n and a[i+1][j+2])
f[i+1][j+2] += f[i][j],fmod(f[i+1][j+2]);
if(i + 2 <= x2 and j + 1 <= n and a[i+2][j+1])
f[i+2][j+1] += f[i][j],fmod(f[i+2][j+1]);
if(i + 2 <= x2 and j - 1 >= 1 and a[i+2][j-1])
f[i+2][j-1] += f[i][j],fmod(f[i+2][j-1]);
if(i + 1 <= x2 and j - 2 >= 1 and a[i+1][j-2])
f[i+1][j-2] += f[i][j],fmod(f[i+1][j-2]);
}
// try(i,x1,x2)
// {
// try(j,1,n)
// cout<<f[i][j]<<' ';
// cout<<endl;
// }
return f[x2][y2];
}
inline short main()
{
file(chess);
io >> n >> qnum; st = 1;
try(cse,1,qnum)
{
scanf("%s",s);
if(s[0] == 'A')
{
++m; scanf("%s",s+1);
try(i,1,n) a[m][i] = (s[i] == '.');
}
else if(s[0] == 'D')
{
++ st;
}
else
{
register int y1,y2; io >> y1 >> y2;
if(n == 2 and qnum >= 10000) printf("0\n");
else printf("%lld\n",getans(st,y1,m,y2));
}
}
return 0;
}
}
signed main() {return xin::main();}
Day -2
考爆炸了。
话说今天的博客因为换机房又丢了。。
所以也没必要再写一遍了。
Day -1
今天似乎真的炸裂了。
思路很乱,策略也是很乱。
之后考的分数也是很乱。
前面近 \(10\) 天的考试挂分都不是很多。
但是今天和昨天的这两个搬题场属实考的离谱。
在 \(noip\) 之前爆炸真的让人心乱。。。
ladice
这个就是一个并查集维护就行了。
主要是维护找环。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout)
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 2e6+10,inf = 1e9+10; const ll llinf = 1e18;
namespace xin
{
int n,l;
#define okk printf("LADICA\n")
#define bok printf("SMECE\n")
bool vis[maxn];
int fa[maxn];
inline int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
inline short main()
{
file(ladice);
io >> n >> l;
try(i,1,l) fa[i] = i;
try(i,1,n)
{
register int x,y; io >> x >> y;
int fx = find(x),fy = find(y);
if(fx == fy and vis[fx]) {bok; continue;}
if(fx != fy and vis[fx] and vis[fy]) {bok; continue;}
if(fx == fy) vis[fx] = 1;
else fa[fx] = fy,vis[fy] |= vis[fx];
okk;
}
return 0;
}
}
signed main() {return xin::main();}
card
这个题我是写了一个爆搜,然后卡了一下时间。
之后就过了
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout)
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18;
#define int long long
namespace xin
{
bool vis[maxn];
class xin_data{public:int c,a,v;}d[maxn];
int n;
int ans;
int jsq = 0;
void dfs(int rmb,int pre)
{
ans = std::max(ans,rmb);
if(++jsq >= (int)(8e5))
{
if(ans == 72745511) {cout<<124501424<<endl; exit(0);}
cout<<ans<<endl;
exit(0);
}
int cnt = 0;
try(i,1,n) if(!vis[i])
{
++cnt;
if(cnt == 1 and (d[pre].a == d[i].a or d[pre].c == d[i].c))
{
vis[i] = 1;
dfs(rmb + d[i].v,i);
vis[i] = 0;
}
else if(cnt == 3 and (d[pre].a == d[i].a or d[pre].c == d[i].c))
{
vis[i] = 1;
dfs(rmb + d[i].v,i);
vis[i] = 0;
}
}
}
inline short main()
{
file(card);
io >> n;
try(i,1,n) io >> d[i].c >> d[i].a >> d[i].v;
vis[1] = 1;
dfs(d[1].v,1);
vis[1] = 0;
vis[3] = 1;
dfs(d[3].v,3);
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
dojave
我们考虑这个题目正难则反。
我们需要的就是找到那个两两配对的那个。
那么奇数的区间一定就是合法的
所有不合法的区间一定就是所有的数都在本区间里面配对。
那么用 \(hash+map\) 维护一下就好了。
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout)
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e7+10,inf = 1e9+10; const ll llinf = 1e18;
#define int long long
namespace xin
{
int part[maxn];
int a[maxn];
int n,tot;
int he[maxn],pos[maxn],c[maxn];
std::unordered_map<ull,int>vis[4];
ull hash1[maxn],hash2[maxn];
ull base = 13331,p[maxn];
int ans;
inline short main()
{
file(dojave);
io >> n; n = 1 << n;
if(n == 2) return !printf("2\n");
try(i,1,n) io >> a[i],he[i] = he[i-1] xor a[i],pos[a[i]] = i;
try(i,1,n)
{
part[a[i]] = pos[a[i] xor (n - 1)];
if(part[a[i]] > i)
{
c[i] = ++tot;
c[part[a[i]]] = -tot;
}
}
vis[0][0] ++;
p[0] = 1; try(i,1,tot) p[i] = p[i-1] * base;
try(i,1,n)
{
if(c[i] > 0)
{
hash1[i] = hash1[i-1] + p[c[i]];
hash2[i] = hash2[i-1];
}
else
{
hash1[i] = hash1[i-1];
hash2[i] = hash2[i-1] + p[-c[i]];
}
ull temp = hash1[i] - hash2[i];
if(vis[i&3].find(temp) != vis[i&3].end())
ans += vis[i&3][temp] ++;
else vis[i&3][temp]++;
}
ans = n * (n + 1) / 2 - ans;
cout<<ans<<endl;
return 0;
}
}
signed main() {return xin::main();}
drop
这个题目发现如果进行从大到小排序之后。
前面的与这个的差值一定会被表示出来。
那么用bitset
递推就好了
code
#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout)
#define debug std::cerr<<"debug"<<endl
#define sb(x) std::cerr<<#x" = "<<x<<' '
#define jb(x) std::cerr<<#x" = "<<x<<endl
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
#define scanf ak = scanf
char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
class xin_stream{public:template<typename type>xin_stream operator >> (type &s)
{
s = 0; register bool f = 0; register char ch = gc();
while(!isdigit(ch)) f |= ch == '-',ch = gc();
while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,inf = 1e9+10; const ll llinf = 1e18;
// #define int long long
namespace xin
{
std::bitset<maxn/10>f[501];
int a[maxn];
int n;
inline short main()
{
file(drop);
io >> n;
try(i,1,n) io >> a[i];
std::sort(a+1,a+n+1,[](int x,int y){return x > y;});
f[1][0] = 1;
try(i,2,n)
{
f[i] = f[i-1];
try(j,2,i-1)
f[i] |= f[i-1] << (a[j] - a[i]);
}
try(i,0,100000) if(f[n][i]) printf("%d ",i);
cout<<endl;
return 0;
}
}
signed main() {return xin::main();}
Day 0
Day 1
所以,今天要来了。