考试就在我们学校考…
下午还被拉去军体拳比赛舞旗。(实际上前两天都有被拉去训练)
晚上教练说就不回家了,在学校自习。然后有位巨佬溜了…其他人上到10点也溜了。只有我一个人是住读,10:50溜回寝室睡觉。
大概11:40睡着吧
考试就咕掉了每天的英语/语文早读。
然后7:20去了机房,发现考场门大开,没人。挺离谱的。
在没作考场的机房看了看模板,过了一会被赶下楼说7:50才能上楼。。。。
进考场。看题。
10分钟发现好像是个傻逼题,线段树上二分。 O ( n log n ) O(n\log n) O(nlogn)。
写到一半发现相同答案求最大位置有点难搞。还得再去线段树上二分一次。
然后去看T2,发现不会,只有30分。去看T3。
发现集合外的点如果能替代就连边,然后知道最终的大小关系。想起来就是一个保序回归问题的 L 2 L_2 L2。
但是我从来没打过啊!
就看了论文的其中一节。
看看时间10点,还有3h,搏一搏单车变摩托。
然后就开写,整体二分+最小割。割边的值我直接设为修改代价。不知道对不对。
写到一半发现如果要满足题目还有不只每个点的大小,还有很多点线性组合的大小关系,然后有点懵逼。举了几个例子发现只考虑单点应该是对的。。。然后继续写。
写代码时肩好酸,舞旗太卖力了。
写完11.30。不过样例。完了。
调了一会发现是网络流写错了,变量重名。
居然过样例了。
大样例也过了。。。。一看大样例n=800,数据范围是1000,感觉应该大概螺旋稳(bushi)。反正暴力也写不来。就这样了。。。
回去写T1暴力,对拍,发现T1写错了。心态爆炸。
发现是数据造错了。改过来 拍上了。
(然后我nt没有测最大数据跑的时间,想着 O ( n log n ) O(n\log n) O(nlogn)应该是复杂度下限了)
过了一会发现T2的m=0能写。多10分,变成40。
想了一会没想到 k i k^i ki怎么处理。不会了。
虚假的估分:100+40+100
下来问了问,好像zjx,wk都ak了。太强了。
然后xmy切T2了,太强了。
csq找出斯特林数的规律切T2了,太强了。
然后听说T1卡常,不开O2,本地要跑6、7秒。
有点慌。
教练问我T1卡不卡常,其实我没测,但是我支支吾吾应付过去了。
下午考场极域用不起,老师叫我们帮忙手动关机。然后我去自己的机子上改一下对拍的数据范围,测一测。
跑不出来。心肺停止。
过了一会发现是造数据数组忘了开大到2000000。。。然后测了一下跑了7、8秒。
感觉被卡常了。
问了问 log 2 \log^2 log2的都只跑7s。感觉药丸。我还没写fread和快速输出。
要是挂到60就人没了。
本来CSP考的很菜。如果T3爆0,T1爆60,就只有60+40+0=100。当场退役。
看人品吧。估计会挂。
(退役也挺好,回去快乐文化课(bu shi))
下来看了一下wk大爷的T2做法,好神仙。但是uoj群都说T2一眼题。我太菜了还是退役吧。
原来T2的次幂可以转化成下降幂,然后下降幂用求导处理。QwQ。
@退役预定@ \Large\texttt{@退役预定@} @退役预定@
退役成功
T1 70 不会卡空间
T2 10分 傻逼原题不会
T3 50分 尝试写70没调出样例(自己出的)
舒服了 直接退役
洛谷上测了一下好像是:70 + 20 + 70
实际T2一定只有10分,如果T3运气好70,总分就是150。
貌似退役了 压线15名 但被女选手名额卡了
文化课我来了 Q . Q Q.Q Q.Q
放一下考场写的或者下来改了的代码:
单点修改,查询两次线段树二分
洛谷上开O2能过 O ( n log n ) O(n\log n) O(nlogn)
不小心开了long long
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
void read(int &x) {
char ch; while(!isdigit(ch=getchar()));
for(x=ch-'0';isdigit(ch=getchar());x=x*10+ch-'0');
}
const int MAXN = 2000010;
int q, bin[MAXN], n;
struct node { int op, t, x, y; }a[MAXN];
LL sum[2][MAXN<<2];
void ins(int i, int l, int r, int x, int t, int v) {
sum[t][i] += v;
if(l == r) return;
int mid = (l + r) >> 1;
if(x <= mid) ins(i<<1, l, mid, x, t, v);
else ins(i<<1|1, mid+1, r, x, t, v);
}
int find1(int i, int l, int r, LL s) {
if(l == r) return l;
int mid = (l + r) >> 1;
if(sum[1][i<<1|1] >= s) return find1(i<<1|1, mid+1, r, s);
return find1(i<<1, l, mid, s-sum[1][i<<1|1]);
}
struct mynode { LL ls, rs, val; mynode(LL l=0, LL r=0) { val = 2*min(ls=l, rs=r); } };
inline mynode gx(const mynode &A, const mynode &B) {
return A.val == B.val ? (A.rs < B.rs ? A : B) : (A.val > B.val ? A : B);
}
mynode qry(int i, int l, int r, LL vl, LL vr) {
if(l == r) return mynode(vl, vr+sum[1][i]);
int mid = (l + r) >> 1;
mynode re = mynode(vl+sum[0][i<<1], vr+sum[1][i<<1|1]);
if(vl+sum[0][i<<1] <= vr+sum[1][i<<1|1]) re = gx(re, qry(i<<1|1, mid+1, r, vl+sum[0][i<<1], vr));
else re = gx(re, qry(i<<1, l, mid, vl, vr+sum[1][i<<1|1]));
return re;
}
int main () {
freopen("icefire.in", "r", stdin);
freopen("icefire.out", "w", stdout);
read(q); bin[++n] = 0;
for(int i = 1; i <= q; ++i) {
read(a[i].op); --a[i].op;
if(!a[i].op)
read(a[i].t), read(a[i].x), read(a[i].y), bin[++n] = a[i].x;
else read(a[i].t);
}
sort(bin + 1, bin + n + 1);
n = unique(bin + 1, bin + n + 1) - bin - 1;
bin[n+1] = bin[n] + 1; ++n;
for(int i = 1; i <= q; ++i) {
if(!a[i].op) {
a[i].x = lower_bound(bin + 1, bin + n + 1, a[i].x) - bin;
ins(1, 1, n, a[i].x-(!a[i].t), a[i].t, a[i].y);
}
else {
int p = a[i].t;
ins(1, 1, n, a[p].x-(!a[p].t), a[p].t, -a[p].y);
}
mynode ans = qry(1, 1, n, 0, 0);
if(ans.val) {
int pos = find1(1, 1, n, ans.rs);
printf("%d %lld\n", bin[pos], ans.val);
}
else puts("Peace");
}
}
直接用第二类斯特林和组合数展开 k i k_i ki,然后推式子,不需要其他高级技巧。考试时没想到。 O ( m 2 ) O(m^2) O(m2)
这道题是下来改的。
#include
using namespace std;
const int MAXM = 1005;
int pw[MAXM], C[MAXM], S[MAXM][MAXM];
int n, x, p, m;
inline int qpow(int a, int b) {
int re = 1;
for(; b; b>>=1, a=1ll*a*a%p)if(b&1)re=1ll*re*a%p;
return re;
}
void pre() {
C[0] = S[0][0] = 1;
for(int i = 1; i <= m; ++i) {
C[i] = 1ll * C[i-1] * (n-i+1) % p;
for(int j = 1; j <= m; ++j)
S[i][j] = (S[i-1][j-1] + 1ll * S[i-1][j] * j) % p;
}
int up = min(n, m); pw[up] = qpow(x+1, n-up);
for(int i = up-1; i >= 0; --i) pw[i] = 1ll * pw[i+1] * (x + 1) % p;
}
int main () {
scanf("%d%d%d%d", &n, &x, &p, &m); pre();
int ans = 0;
for(int i = 0, a; i <= m; ++i) {
scanf("%d", &a); int t = 1;
for(int j = 0; j <= i; ++j) {
ans = (ans + 1ll * S[i][j] * C[j] % p * t % p * pw[j] % p * a) % p;
t = 1ll * t * x % p;
}
}
printf("%d\n", ans);
}
猜结论:如果一个点能被集合外的替代就连边然后 L 2 L_2 L2保序回归。判断暴力线性基。不知道能不能过。反正过了大样例。
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
template<class T>void read(T &x) {
char ch; while(!isdigit(ch=getchar()));
for(x=ch-'0';isdigit(ch=getchar());x=x*10+(ch-'0'));
}
const int MAXN = 1010;
const int MAXM = 70;
const int MAXE = MAXN*MAXM*2;
const int mxxxe = (MAXE+2000)*2;
const LL inf = 1ll<<50;
int n, m, tot, v[MAXN], a[MAXN], b[MAXN];
bool ina[MAXN], inb[MAXN];
ULL c[MAXN];
struct xxj {
ULL p[65];
void clear() { for(int i = 0; i < 64; ++i) p[i] = 0; }
bool ins(ULL x) {
for(int i = 0; i < 64; ++i)
if(x>>i&1) {
if(!p[i]) { p[i] = x; return 1; }
x ^= p[i];
}
return 0;
}
bool mytry(ULL x) {
for(int i = 0; i < 64; ++i)
if(x>>i&1) {
if(!p[i]) return 1;
x ^= p[i];
}
return 0;
}
}tmp;
struct edge { int u, v; }E[MAXE], tmpE[MAXE];
struct node { int i; }pt[MAXN], tmppt[MAXN];
int ans[MAXN];
int fir[MAXN], info[MAXN], to[mxxxe], nxt[mxxxe], cnt, S, T;
LL cp[mxxxe];
inline void mylink(int u, int v, LL cc) {
to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; cp[cnt] = cc;
to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt; cp[cnt] = 0;
}
inline LL sqr(LL x) { return x * x; }
int myq[MAXN], dis[MAXN];
bool bfs() {
int ql = 0, qr = 0;
memset(dis, -1, (T+1)<<2);
dis[T] = 0; myq[qr++] = T;
while(ql < qr) {
int u = myq[ql++];
for(int i = fir[u], v; i; i = nxt[i])
if(cp[i^1] && !~dis[v=to[i]])
dis[v] = dis[u] + 1, myq[qr++] = v;
}
return ~dis[S];
}
LL aug(int u, LL Max) {
if(u == T) return Max;
LL re = 0, delta;
for(int v, &i = info[u]; i; i = nxt[i])
if(cp[i] && dis[v=to[i]] + 1 == dis[u] && (delta = aug(v, min(Max-re, cp[i])))) {
cp[i] -= delta, cp[i^1] += delta;
if((re += delta) == Max) return Max;
}
return re;
}
void maxflow() {
while(bfs())
memcpy(info, fir, (T+1)<<2), aug(S, inf);
}
bool flg[MAXN];
void dfs(int u) {
flg[u] = 1;
for(int i = fir[u], v; i; i = nxt[i])
if(!flg[v=to[i]] && cp[i]) dfs(v);
}
void solve(int vl, int vr, int pl, int pr, int el, int er) {
if(pl > pr) return;
if(vl == vr) {
for(int i = pl; i <= pr; ++i) ans[pt[i].i] = vl;
return;
}
int mid = (vl + vr) >> 1;
cnt = 1; S = n+1, T = n+2; fir[S] = fir[T] = 0;
for(int i = pl; i <= pr; ++i) {
fir[pt[i].i] = 0; flg[pt[i].i] = 0;
mylink(S, pt[i].i, sqr(v[pt[i].i]-(mid+1))), mylink(pt[i].i, T, sqr(v[pt[i].i]-mid));
}
for(int i = el; i <= er; ++i) mylink(E[i].v, E[i].u, inf);
maxflow(); dfs(S);
int pL = pl, pR = pr;
for(int i = pl; i <= pr; ++i)
if(flg[pt[i].i]) tmppt[pL++] = pt[i];
else tmppt[pR--] = pt[i];
for(int i = pl; i <= pr; ++i) pt[i] = tmppt[i];
int eL = el, eR = er;
for(int i = el; i <= er; ++i)
if(flg[E[i].u] && flg[E[i].v]) tmpE[eL++] = E[i];
else if(!flg[E[i].u] && !flg[E[i].v])tmpE[eR--] = E[i];
for(int i = el; i <= er; ++i) E[i] = tmpE[i];
solve(vl, mid, pl, pL-1, el, eL-1);
solve(mid+1, vr, pR+1, pr, eR+1, er);
}
int main () {
freopen("shop.in", "r", stdin);
freopen("shop.out", "w", stdout);
read(n), read(m);
int vmn = 1000000, vmx = 0;
for(int i = 1; i <= n; ++i) read(c[i]), pt[i].i = i;
for(int i = 1; i <= n; ++i) read(v[i]), vmn = min(vmn, v[i]), vmx = max(vmx, v[i]);
for(int i = 1; i <= m; ++i) read(a[i]), ina[a[i]] = 1;
for(int i = 1; i <= m; ++i) read(b[i]), inb[b[i]] = 1;
for(int i = 1; i <= m; ++i) {
int x = a[i]; tmp.clear();
for(int j = 1; j <= m; ++j)
if(j != i) tmp.ins(c[a[j]]);
for(int j = 1; j <= n; ++j)
if(!ina[j] && tmp.mytry(c[j]))
E[++tot] = (edge) { x, j };
}
for(int i = 1; i <= m; ++i) {
int x = b[i]; tmp.clear();
for(int j = 1; j <= m; ++j)
if(j != i) tmp.ins(c[b[j]]);
for(int j = 1; j <= n; ++j)
if(!inb[j] && tmp.mytry(c[j]))
E[++tot] = (edge) { j, x };
}
solve(vmn, vmx, 1, n, 1, tot);
LL res = 0;
for(int i = 1; i <= n; ++i) res += sqr(v[i]-ans[i]);
printf("%lld\n", res);
}
考试只会空间 O ( m 2 m ) O(m2^m) O(m2m)。然后发现可以优化。从小到大枚举 S S S其实相当于在遍历一个 D A G DAG DAG,可以用一个栈存一下从起点到当前 S S S的路径,然后枚举下一个 S ′ S' S′就弹栈直到栈顶是 S ′ S' S′的子集,这个时候栈顶和 S ′ S' S′只会相差 l o w b i t ( S ′ ) lowbit(S') lowbit(S′)。那么每个深度存两个 O ( m ) O(m) O(m)的数组表示 i i i到当前栈元素集合的出/入边。加入栈的时候直接从上一个深度的数组上继承过来然后加上 l o w b i t lowbit lowbit的贡献。
空间就是 O ( m 2 ) O(m^2) O(m2)的,时间还是 O ( 2 m m ) O(2^mm) O(2mm)。
考试时只写了 m < = 21 m<=21 m<=21,下面是后来改的代码
#include
#include
#include
#include
#include
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef unsigned long long ULL;
template<class T>inline void read(T &x) {
char ch; int flg=1; while(!isdigit(ch=getchar()))if(ch=='-')flg=-flg;
for(x=ch-'0'; isdigit(ch=getchar()); x=x*10+ch-'0'); x *= flg;
}
const int MAXN = 23;
const int MAXS = 1<<23;
int n, m, k, E[MAXN][MAXN], in[MAXN], out[MAXN];
int sz[MAXS], Lg[MAXS], dp[MAXS];
inline void chkmin(int &x, int y) { y < x ? x = y : 0; }
int stk[MAXN+1], indx, f[MAXN+1][MAXN], g[MAXN+1][MAXN];
int main () {
//freopen("transfer.in", "r", stdin);
//freopen("transfer.out", "w", stdout);
read(n), read(m), read(k);
for(int i = 1, x, y = -1; i <= n; ++i) {
read(x); --x;
if((~y) && x != y) ++E[y][x], ++in[x], ++out[y];
y = x;
}
int all = (1<<m)-1;
memset(dp, 0x3f, sizeof dp); dp[0] = 0;
stk[++indx] = 0; Lg[0] = -1;
for(int s = 0; s < all; ++s) {
sz[s] = sz[s>>1] + (s&1); int p = sz[s]+1;
if(s) {
Lg[s] = Lg[s>>1] + 1;
while(indx && (stk[indx] | s) != s) --indx;
stk[++indx] = s;
for(int i = 0, lb = Lg[s&-s]; i < m; ++i)
f[indx][i] = f[indx-1][i] + E[i][lb],
g[indx][i] = g[indx-1][i] + E[lb][i];
}
for(int i = 0; i < m; ++i) if(!(s>>i&1))
chkmin(dp[s^(1<<i)], dp[s] + (f[indx][i] + (in[i] - g[indx][i])) * k * p + (g[indx][i] - (out[i] - f[indx][i])) * p);
}
printf("%d\n", dp[all]);
}
傻逼题。套路考过。考试时根本没想起来。思维僵化。直接退役。
本来考场应该能想出来,已经在分析 x − > x + 1 x->x+1 x−>x+1的变化,按位考虑嫌麻烦没细想,其实很简单。
trie树所有值加一就从低到高建trie树,整体加一就是从根开始交换两个儿子,然后往交换后的0那一边递归。
改过了。
#include
#include
#include
#include
#include
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef unsigned long long ULL;
template<class T>inline void read(T &x) {
char ch; int flg=1; while(!isdigit(ch=getchar()))if(ch=='-')flg=-flg;
for(x=ch-'0'; isdigit(ch=getchar()); x=x*10+ch-'0'); x *= flg;
}
const int MAXN = 525050;
const int MAXM = MAXN * 40;
int n, val[MAXN], fa[MAXN], fir[MAXN], to[MAXN], nxt[MAXN], cnt;
inline void link(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; }
int s, rt[MAXN], tot, sz[MAXM], ch[MAXM][2], sum[MAXM];
long long ans;
void ins(int &x, int v, int d) {
if(!x) x = ++tot; ++sz[x];
if(d == 22) return;
ins(ch[x][v>>d&1], v, d+1);
sum[x] = sum[ch[x][0]] ^ sum[ch[x][1]] ^ ((sz[ch[x][1]]&1)<<d);
}
void mdf(int x, int d) {
if(!x || d == 22) return;
swap(ch[x][0], ch[x][1]); mdf(ch[x][0], d+1);
sum[x] = sum[ch[x][0]] ^ sum[ch[x][1]] ^ ((sz[ch[x][1]]&1)<<d);
}
void merge(int &u, int v, int d) {
if(!u || !v) { u = u + v; return; }
sz[u] += sz[v];
if(d == 22) return;
merge(ch[u][0], ch[v][0], d+1);
merge(ch[u][1], ch[v][1], d+1);
sum[u] = sum[ch[u][0]] ^ sum[ch[u][1]] ^ ((sz[ch[u][1]]&1)<<d);
}
void dfs(int u) {
for(int i = fir[u], v; i; i = nxt[i])
dfs(v=to[i]), merge(rt[u], rt[v], 0);
mdf(rt[u], 0);
ins(rt[u], val[u], 0);
ans += sum[rt[u]];
}
int main () {
//freopen("tree.in", "r", stdin);
//freopen("tree.out", "w", stdout);
read(n);
for(int i = 1; i <= n; ++i) read(val[i]);
for(int i = 2; i <= n; ++i) read(fa[i]), link(fa[i], i);
dfs(1);
printf("%lld\n", ans);
}
考试写了70。
O ( n 5 d ) O(n^5d) O(n5d)的做法考场上想到没写,结果居然能过。草。
下面写了 O ( n 4 d ) O(n^4d) O(n4d)的解法:
#include
#include
#include
#include
using namespace std;
#define pb push_back
const int mod = 998244353;
const int MAXN = 33;
const int MAXM = 450;
inline int qpow(int a, int b) {
int re = 1;
for(; b; b>>=1, a=1ll*a*a%mod)if(b&1)re=1ll*re*a%mod;
return re;
}
int n, m;
struct edge { int u, v, w; }E[MAXM];
inline int upd(int x) { return x + (x >> 31 & mod); }
struct node {
int a, b; //a + bx
node(int a=0, int b=0):a(a), b(b){}
inline node operator +(const node &o)const { return node(upd(a+o.a-mod), upd(b+o.b-mod)); }
inline node operator -(const node &o)const { return node(upd(a-o.a), upd(b-o.b)); }
inline node operator *(const node &o)const { return node(1ll*a*o.a%mod, (1ll*a*o.b+1ll*b*o.a)%mod); }
inline node operator *(const int &o)const { return node(1ll*a*o%mod, 1ll*b*o%mod); }
}a[MAXN][MAXN];
int calcu() {
node re(1, 0);
for(int j = 1; j < n; ++j) {
if(!a[j][j].a) {
for(int i = j+1; i < n; ++i)
if(a[i][j].a) { swap(a[i], a[j]); re = re * (mod-1); break; }
if(!a[j][j].a && !a[j][j].b)
for(int i = j+1; i < n; ++i)
if(a[i][j].b) { swap(a[i], a[j]); re = re * (mod-1); break; }
}
if(!a[j][j].a && !a[j][j].b) return 0;
re = re * a[j][j];
if(a[j][j].a) {
node iv = node(a[j][j].a, mod-a[j][j].b) * qpow(a[j][j].a, mod-3);
for(int k = j; k < n; ++k) a[j][k] = a[j][k] * iv;
for(int i = j+1; i < n; ++i) if(a[i][j].a || a[i][j].b)
for(int k = n-1; k >= j; --k)
a[i][k] = (a[i][k] - a[j][k] * a[i][j]);
}
else {
node iv = node(qpow(a[j][j].b, mod-2), 0);
for(int k = j; k < n; ++k) a[j][k] = a[j][k] * iv;
for(int i = j+1; i < n; ++i) if(a[i][j].a || a[i][j].b)
for(int k = n-1; k >= j; --k)
a[i][k] = (a[i][k] - a[j][k] * a[i][j].b);
}
}
return re.b;
}
const int MAXV = 152501+10;
int pr[MAXV], cnt_pr, phi[MAXV]; bool vis[MAXV];
vector<int>vec[MAXV];
void pre(int N) {
phi[1] = 1;
for(int i = 2; i <= N; ++i) {
if(!vis[i]) phi[i] = i-1, pr[++cnt_pr] = i;
for(int j = 1; j <= cnt_pr && i*pr[j] <= N; ++j) {
vis[i*pr[j]] = 1;
if(i % pr[j] == 0) { phi[i*pr[j]] = phi[i] * pr[j]; break; }
phi[i*pr[j]] = phi[i] * (pr[j]-1);
}
}
}
int main () {
//freopen("count.in", "r", stdin);
//freopen("count.out", "w", stdout);
scanf("%d%d", &n, &m); int mxv = 0;
for(int i = 1; i <= m; ++i) {
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
mxv = max(mxv, E[i].w);
for(int j = 1; j*j <= E[i].w; ++j) if(E[i].w % j == 0) {
vec[j].pb(i);
if(E[i].w != j*j) vec[E[i].w/j].pb(i);
}
}
pre(mxv);
int ans = 0;
for(int i = 1; i <= mxv; ++i) if((int)vec[i].size() >= n-1) {
for(int j = 1; j < n; ++j)for(int k = 1; k < n; ++k)a[j][k] = 0;
for(int j = vec[i].size()-1; j >= 0; --j) {
node val = node(1, E[vec[i][j]].w); int u = E[vec[i][j]].u, v = E[vec[i][j]].v;
a[u][u] = a[u][u] + val;
a[v][v] = a[v][v] + val;
a[u][v] = a[u][v] - val;
a[v][u] = a[v][u] - val;
}
ans = (ans + 1ll * calcu() * phi[i]) % mod;
}
printf("%d\n", ans);
}