T1 Dove 打扑克
sol
先想暴力做法:合并直接用并查集统计即可,查询暴力扫一遍处理前缀和,在反着扫一遍计算答案, \(c=0\) 的时候特判。
然后想怎么才能优化:在查询的过程中其实有大量的数是\(0\),也就是说其实很多地方可以优化。具体地,简单计算不难发现:至多有\(\sqrt n\)个值非零,所以可以使用\(vector\)来维护这些非零的数即可。在修改的时候暴力维护保证其单调性,查询同暴力,时间复杂度\(O(m\sqrt n)\)。
但是因为rp原因我挂点了。。。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
const int maxn=100010;
int n,m,a[maxn],siz[maxn],fa[maxn];
int sum[maxn];
struct node
{
int dx,c;
};
vector<node>vc;
inline int findf(int x)
{
if(fa[x]==x)return x;
return fa[x]=findf(fa[x]);
}
inline void uion(int x,int y)
{
x=findf(x),y=findf(y);
if(x==y)return;
fa[x]=y;bool flag=0;
for(int i=0;i<vc.size();i++)
{
if(vc[i].dx==siz[x])
{
vc[i].c--;if(vc[i].c==0)vc.erase(vc.begin()+i);
}
if(vc[i].dx==siz[y])
{
vc[i].c--;if(vc[i].c==0)vc.erase(vc.begin()+i);
}
if(vc[i].dx==siz[x]+siz[y])
{
flag=1;vc[i].c++;
break;
}
if(vc[i].dx>siz[x]+siz[y]&&!flag)
{
flag=1;vc.insert(vc.begin()+i,(node){siz[x]+siz[y],1});
break;
}
}
if(!flag)vc.push_back((node){siz[x]+siz[y],1});
siz[y]+=siz[x];
return;
}
signed main()
{
freopen("cards.in","r",stdin);
freopen("cards.out","w",stdout);
n=read();m=read();
vc.push_back((node){1,n});
for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
while(m--)
{
int opt=read();
if(opt==1)
{
int x=read(),y=read();
uion(x,y);
}else
{
int c=read(),ans=0;
int sz=vc.size();
if(c==0)
{
for(int i=0;i<sz;i++)ans+=vc[i].c*(vc[i].c-1)/2;
c=1;
}
for(int i=sz-1;i>=0;i--)sum[i]=sum[i+1]+vc[i].c;
int j=0;
for(int i=0;i<sz;i++)
{
for(;vc[j].dx<vc[i].dx+c;j++);
ans+=sum[j]*vc[i].c;
}
printf("%lld\n",ans);
}
}
return 0;
}
T2 Cicada 与排序
sol
部分分不会。正解由wwlw神提供。
显然,不同的数的区间范围是确定的。那么我们就可以单独处理每个数值然后加上它在整体中的位置即可。
对于一个数值,我们可以把它单独的归并树画出来,并像线段树那样标号。设\(dp[i][j]\)表示在树上标号为\(i\)的点期望排第\(j\)的数最终排第几。那么显然有\(dp[1][j]=j\)。
接下来考虑如何向下转移:假定\(i\)的子节点为\(l\)和\(r\),\(siz[x]\)表示\(x\)节点的数的个数。
不妨先考虑\(dp[l][j]\),则它的期望就是
表示模拟归并中二选一的机会概率。
但是还漏了一种情况:\(r\)中所有的数都已经出队,也就是说剩下的没得选全部是\(l\)中的,这部分的期望就是
最终转移就是这两个之和。单次复杂度\(O(n^2)\),当不同的数越多效率越高,最高\(O(n*log(n))\)。这个算法的复杂度是优于\(std\)的。
code啊晚上摸鱼去了没写哦。
T3 Cicada 拿衣服
sol
考场降智,乱写了一个\(O(n^3log(n))\)的暴力线段树,结果居然过了\(n=10^3\)的数据,喜提24分就爪巴了。
一个\(O(n^2)\)的做法:暴力枚举开头,然后暴力向后维护,如果当前合法就更新一次最长长度。然后把范围内更新一次,常数很小,小到卡过了\(n=30000\)。
\(n\)更大的情况肯定是随机数据啊,所以赌一手最长长度不超过\(700\),然后一波乱卡用\(O(700n)\)喜提\(84\)分。
另:\(std\)也被卡了,喜提\(92\)分。正解我真的不懂。。。
code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
int n,a[1000010],ans[1000010];
int mx,mi,o,an,ri,len,k;
int main()
{
freopen("naive.in","r",stdin);
freopen("naive.out","w",stdout);
n=read();k=read();
for(int i=1;i<=n;i++)a[i]=read();
if(n<=30000)
{
for(int i=1;i<=n;i++)
{
mx=mi=o=an=a[i];ri=0;
for(int j=i+1;j<=n;j++)
{
if(a[j]>mx)mx=a[j];
else if(a[j]<mi)mi=a[j];
o|=a[j];an&=a[j];
if(o+mi-mx-an>=k)ri=j;
}
if(ri)
{
len=ri-i+1;
for(int j=i;j<=ri;j++)if(ans[j]<len)ans[j]=len;
}
}
for(int i=1;i<=n;i++)printf("%d ",ans[i]?ans[i]:-1);
return 0;
}
for(int i=1;i<=n;i++)
{
int upp=min(i+700,n);
mx=mi=o=an=a[i];ri=0;
for(int j=i+1;j<=upp;j++)
{
if(a[j]>mx)mx=a[j];
else if(a[j]<mi)mi=a[j];
o|=a[j];an&=a[j];
if(o+mi-mx-an>=k)ri=j;
}
if(ri)
{
len=ri-i+1;
for(int j=i;j<=ri;j++)if(ans[j]<len)ans[j]=len;
}
}
for(int i=1;i<=n;i++)printf("%d ",ans[i]?ans[i]:-1);
return 0;
}
先粘一个\(std\)的代码上来,等会再想原理。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define up(i,j,n) for (int i = j; i <= n; i++)
#define down(i,j,n) for (int i = j; i >= n; i--)
#define cadd(a,b) a = add (a, b)
#define cpop(a,b) a = pop (a, b)
#define cmul(a,b) a = mul (a, b)
#define pr pair<int, int>
#define fi first
#define se second
#define SZ(x) (int)x.size()
#define bin(i) (1 << (i))
#define Auto(i,node) for (int i = LINK[node]; i; i = e[i].next)
#define FILE "naive"
template<typename T> inline bool cmax(T & x, T y){return y > x ? x = y, true : false;}
template<typename T> inline bool cmin(T & x, T y){return y < x ? x = y, true : false;}
template<typename T> inline T dmax(T x, T y){return x > y ? x : y;}
template<typename T> inline T dmin(T x, T y){return x < y ? x : y;}
const int MAXN = 1e6 + 5;
const int oo = 0x3f3f3f3f;
namespace IO {
const int BUF = bin(20);
char buf[BUF], *fs, *ft;
inline char getc(){
if (fs == ft) {
fs = buf;
ft = fs + fread(buf, 1, BUF, stdin);
}
return *fs++;
}
inline int read(){
char ch = getc(); int x = 0, f = 1;
while (ch > '9' || ch < '0') {if (ch == '-') f = -1; ch = getc();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getc();}
return x * f;
}
}
using IO::read;
int N, K, a[MAXN];
struct key {
int le, ri, _or, _and, nxt;
inline key (int le = 0, int ri = 0, int _or = 0, int _and = 0, int nxt = 0) :
le(le), ri(ri), _or(_or), _and(_and), nxt(nxt) {}
inline bool operator == (const key & w) const { return _or == w._or && _and == w._and; }
}seg[MAXN];
int st = 0, ed = 0, cnt = 0;
void fix(){
for (int cur = st; cur != -1; cur = seg[cur].nxt) {
if (seg[cur].nxt == -1) break;
if (seg[cur] == seg[seg[cur].nxt]) {
if (ed == seg[cur].nxt) ed = cur;
seg[cur].ri = seg[seg[cur].nxt].ri;
seg[cur].nxt = seg[seg[cur].nxt].nxt;
}
}
}
void play(int val){
for (int cur = st; cur != -1; cur = seg[cur].nxt) {
seg[cur]._or |= val;
seg[cur]._and &= val;
}
}
void add(int o, int v) {
seg[++cnt] = key(o, o, v, v, -1);
seg[ed].nxt = cnt;
ed = cnt;
}
int mx[MAXN][21], mn[MAXN][21], LG[MAXN];
int getd(int le, int ri) { // delta = max{le, ri} - min{le, ri}
int len = ri - le + 1;
len = LG[len];
int d = dmin(mn[le][len], mn[ri - bin(len) + 1][len]) -
dmax(mx[le][len], mx[ri - bin(len) + 1][len]);
return d;
}
vector<pr> w, event; // maybe answer
multiset<int> ss;
int main(){
freopen(FILE".in", "r", stdin);
freopen(FILE".out", "w", stdout);
N = read(); K = read();
up (i, 1, N) a[i] = read();
up (i, 1, N) mx[i][0] = mn[i][0] = a[i];
up (j, 1, 20) up (i, 1, N) {
if (i + bin(j) - 1 > N) break;
mx[i][j] = dmax(mx[i][j - 1], mx[i + bin(j - 1)][j - 1]);
mn[i][j] = dmin(mn[i][j - 1], mn[i + bin(j - 1)][j - 1]);
}
int o = 0;
up (i, 1, N) {
if (bin(o + 1) <= i) o++;
LG[i] = o;
}
st = ed = cnt = 1;
seg[st] = key(1, 1, a[1], a[1], -1);
if (K <= 0) w.push_back(make_pair(1, 1));
up (ri, 2, N) {
add(ri, a[ri]);
play(a[ri]);
fix();
for (int cur = st; cur != -1; cur = seg[cur].nxt) {
int le = seg[cur].le, mi = seg[cur].ri;
if (seg[cur]._or - seg[cur]._and + getd(mi, ri) >= K) {
int dl = le, dr = mi, dm;
while (dl + 1 < dr) {
dm = (dl + dr) >> 1;
if (seg[cur]._or - seg[cur]._and + getd(dm, ri) >= K) dr = dm;
else dl = dm;
}
up (i, dl, dr) if (seg[cur]._or - seg[cur]._and + getd(i, ri) >= K) {
w.push_back(make_pair(i, ri));
// printf("(%d %d)\n", i, ri);
break;
}
break;
}
}
}
up (i, 0, SZ(w) - 1) event.push_back(make_pair(w[i].fi, w[i].se - w[i].fi + 1)),
event.push_back(make_pair(w[i].se + 1, w[i].fi - w[i].se - 1));
if (!event.empty()) sort(event.begin(), event.end());
o = 0;
up (i, 1, N) {
while (o <= SZ(event) - 1 && event[o].fi == i) {
if (event[o].se > 0) ss.insert(event[o].se);
else ss.erase(ss.find(-event[o].se));
o++;
}
if (!ss.empty()) printf("%d ", (int)(*ss.rbegin()));
else printf("-1 ");
}
puts("");
return 0;
}