可以说是第一场AGC了,做了三道题之后还有30min,杠了一下D题发现杠不出来,三题滚粗了

rating起步1300+,感觉还是很菜。。。

只有三题水平显然以后还会疯狂--啊(CF的惨痛经历)

改题的感觉似乎还不错因为思维都非常的妙(我根本想不到)

A - Zero-Sum Ranges

开场娱乐大家的小水题,区间和为0的情况存在于sum[R] == sum[L - 1],只要记录一下某一个值的sum出现了多少次就行,懒得离散化直接用map就OK啊

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#define MAXN 200005
#define PLI pair<long long,int>
#define fi first
#define se second
#define mp make_pair
//#define ivorysi
using namespace std;
typedef long long int64;
int N;
int64 A[MAXN];
map<int64,int> mmm;
void Solve() {
scanf("%d",&N);
for(int i = 1 ; i <= N ; ++i) scanf("%lld",&A[i]);
mmm[0] = 1;
int64 ans = 0;
for(int i = 1 ; i <= N ; ++i) {
A[i] += A[i - 1];
ans += mmm[A[i]];
mmm[A[i]] += 1;
}
printf("%lld\n",ans);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}

B - Find Symmetries

把矩阵行循环平移x,列循环平移y后,矩阵关于左上到右下这条对角线对称

矩阵大小是300

然后我就写了个暴力然而我并没发现我的暴力是\(n^4\)然后愉快TLE

我就开始想着优化,发现固定行平移,每平移一列hash值的更改可以O1算出来,check把每行每列hash起来比较,是O(n)的,然后过掉了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#define MAXN 200005
#define PLI pair<long long,int>
#define fi first
#define se second
#define mp make_pair
#define ha 99994711
#define ba 823
//#define ivorysi
using namespace std;
typedef long long int64;
int N;
char s[305][305],b[305][305];
int64 hc[305],hr[305],e;
inline int C(int x) {
return x > N ? x - N : x;
}
bool check(int y) {
int T = C(N + y);
for(int i = 1 ; i <= N ; ++i)
hr[i] = (hr[i] * ba + (b[i][T] - 'a' + 1)) % ha;
for(int i = 1 ; i <= N ; ++i) {
if(hr[i] != hc[C(i + y)]) {
T = C(N + y + 1);
for(int j = 1 ; j <= N ; ++j)
hr[j] = (hr[j] - e * (b[j][T] - 'a' + 1) % ha + ha) % ha;
return false;
}
}
T = C(N + y + 1);
for(int i = 1 ; i <= N ; ++i)
hr[i] = (hr[i] - e * (b[i][T] - 'a' + 1) % ha + ha) % ha;
return true;
}
void Solve() {
scanf("%d",&N);
if(N == 1) {
puts("1");return;
}
e = 1;
for(int i = 1 ; i < N ; ++i) e = e * ba % ha;
for(int i = 1 ; i <= N ; ++i) {
scanf("%s",s[i] + 1);
}
int cnt = 0;
for(int A = 0 ; A < N ; ++A) {
memset(hc,0,sizeof(hc));
memset(hr,0,sizeof(hr));
for(int i = 1 ; i <= N ; ++i) {
for(int j = 1 ; j <= N ; ++j) {
b[i][j] = s[C(i + A)][j];
hc[j] = (hc[j] * ba + (b[i][j] - 'a' + 1)) % ha;
if(j != N) {
hr[i] = (hr[i] * ba + (b[i][j] - 'a' + 1)) % ha;
}
}
}
for(int B = 0 ; B < N ; ++B) {
if(check(B)) ++cnt;
}
}
printf("%d\n",cnt);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}

C - Painting Machines

while(1) 推式子

联想到地震后的幻想乡显然题目可以转化为

\(\sum_{i = 0}^{N - 2} T(i)\)其中\(T(i)\)表示用了i次操作没有全部染黑的方案数

有\(T(0) = (N - 1)!\)

然后分类讨论,一个是因为没用N-1而没有全部染黑的方案数是\(\binom{N - 2}{i}i!\)

如果用了N - 1,那么方案是\((\binom{N - 2}{i - 1} - W(i - 1))i!\)

\(W(i)\)表示用了一个N - 1号机器和其他i个机器,把所有方格染黑了的方案数

显然序列里会有1

把序列差分一下,会发现这个序列不是1就是2,也就是一堆1和一堆2的数目固定,一堆1和一堆2的和是N - 2,这是个二元一次方程,然后求出了1的个数和2的个数,就是个带重复元素的全排列,答案是

\(\frac{(num_1 + num_2)! }{num_1 ! num_2 !}\)

其他\(N - 1 - i\)台机器随意排列,最后还要乘上\((N - i - 1)!\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#define MAXN 1000005
#define PLI pair<long long,int>
#define fi first
#define se second
#define mp make_pair
#define ha 99994711
#define ba 823
#define MOD 1000000007
//#define ivorysi
using namespace std;
typedef long long int64;
int64 fac[MAXN],invfac[MAXN],ans;
int N;
int64 fpow(int64 x,int64 c) {
int64 res = 1,t = x;
while(c) {
if(c & 1) res = res * t % MOD;
t = t * t % MOD;
c >>= 1;
}
return res;
}
int64 C(int n,int m) {
if(n < m) return 0;
return fac[n] * invfac[m] % MOD * invfac[n - m] % MOD;
}
int64 W(int k) {
int x = N - 2 - k;
int y = k - x;
if(x < 0 || y < 0) return 0;
return fac[k] * invfac[y] % MOD * invfac[x] % MOD;
}
void Solve() {
scanf("%d",&N);
if(N == 2) {
puts("1");
return;
}
fac[0] = invfac[0] = 1;
for(int i = 1 ; i <= N ; ++i) {
fac[i] = fac[i - 1] * i % MOD;
}
invfac[N] = fpow(fac[N],MOD - 2);
for(int i = N - 1 ; i >= 1 ; --i) {
invfac[i] = invfac[i + 1] * (i + 1) % MOD;
}
ans += fac[N - 1];
for(int i = 1 ; i < N - 1 ; ++i) {
ans += (C(N - 2,i) + C(N - 2,i - 1) + MOD - W(i - 1))* fac[i] % MOD * fac[N - 1 - i] % MOD;
ans %= MOD;
}
printf("%lld\n",ans);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}

D - Go Home

题解

神仙博弈题(其实也不算博弈)

你看完题,发现,啥,最优决策,咋最优啊,啥最优啊,什么玩意,弃疗吧……

题解是这么说的,如果汽车在1 和N中间,且\(A_1 >= A_n\),那么N肯定会投票给1,为什么,因为赢不了,如果暂时让车靠近了N,最后也会因为N - 1公寓里的人都回家了而车也再回到1,所以为了早回家,N的票都会给1,且最后的操作一定会有一个\(X_{n} - X_{1}\)的长度,那么我们把N的票全部给1,\(P_{1} += P_{N}\)也没有问题,如果\(A_{n} > A_{1}\)是类似的,之后就变成了1和N - 1之间的子问题……停止条件是所有公寓都在初始位置\(S\)的一边

累加答案的话要注意和前一次操作方向相反才累加,如果前一次1 - N,下一次1 - (N - 1),第二次的距离不用累加进答案

代码

#include <iostream>
#include <cstdio>
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
int N;
int64 S,X[MAXN],P[MAXN],ans;
void Solve() {
scanf("%d%lld",&N,&S);
for(int i = 1 ; i <= N ; ++i) {
scanf("%lld%lld",&X[i],&P[i]);
}
int L = 1,R = N;
int dir = 0;
while(1) {
if(X[L] >= S) {ans += X[R] - S;break;}
if(X[R] <= S) {ans += S - X[L];break;}
if(P[L] >= P[R]) {
if(dir != 1) {dir = 1;ans += X[R] - X[L];}
P[L] += P[R];R--;
}
else {
if(dir != 2) {dir = 2;ans += X[R] - X[L];}
P[R] += P[L];L++;
}
}
printf("%lld\n",ans);
} int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}

E - Inversions

我觉得这道题计算排列的方法应该算个比较重要前置技能,但这又不是什么板啥的,如果知道了这个子问题那么这道题至少会有头绪

如何计算每个数位有限制的排列总个数?

先计算一个\(cnt[k]\)表示可以填k的位置的个数,可以用一个后缀和算出来

答案就是\(\prod_{i = 1}^{N} cnt[i] - (N - i)\)

有了这个我们再来看这道题

我们对于两个位置\(i < j\)且\(A_{i} <= A_{j}\)计算\(i\)位置上的数大于\(j\)位置上的数的排列的总和,我们可以让\(A_{j} = A_{i}\),然后计算排列个数,再除二就是答案

以下的\(cnt[i]\)更改成\(cnt[i] - (N - i)\)

这样的话我们考虑更改\(A_{j}\)会使得\([A_{i} + 1,A_{j}]\)这个区间的cnt减1,也就是乘上了\((cnt[i] - 1) / cnt[i]\),也就是处理成区间前缀乘积后相除,然而会发现处理0的情况比较特殊,还需要记录前缀出现了几个0,前缀上0个数相同值才不为0,位置增加0的个数单调不减,所以可以预处理出来每一段开始位置和结束位置

\(A_{i} > A_{j}\)我们发现就是把\(A_{i} := A_{j}\)后总排列数减去计算出的新排列值的一半

以上两种情况都可以用树状数组快速维护

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
const int MOD = 1000000007;
int N,A[MAXN],x[MAXN],st[MAXN],ed[MAXN];
int64 cnt[MAXN],V[MAXN],D[MAXN],ID[MAXN],S,tr[MAXN],tr_cnt[MAXN],Inv_2,ans;
int64 fpow(int64 x,int64 c) {
int64 res = 1,t = x;
while(c) {
if(c & 1) res = res * t % MOD;
t = t * t % MOD;
c >>= 1;
}
return res;
}
int lowbit(int x) {return x & (-x);}
void insert(int x,int64 v) {
while(x <= N) {
tr[x] += v;
tr[x] %= MOD;
tr_cnt[x]++;
x += lowbit(x);
}
}
int64 Query(int x,int64 v) {
int64 res = 0;
while(x > 0) {
res += tr[x];
x -= lowbit(x);
}
res %= MOD;
return res * v % MOD;
}
int64 getnum(int x) {
int64 res = 0;
while(x > 0) {
res += tr_cnt[x];
x -= lowbit(x);
}
return res;
}
int64 Query_range(int L,int R,int64 v) {
if(L == 0) ++L;
if(L > R) return 0;
return (Query(R,v) + MOD - Query(L - 1,v)) % MOD;
}
void Solve() {
scanf("%d",&N);
for(int i = 1 ; i <= N ; ++i) scanf("%d",&A[i]),cnt[A[i]]++;
for(int i = N - 1; i >= 1 ; --i) cnt[i] += cnt[i + 1];
S = 1;Inv_2 = (MOD + 1) / 2;
for(int i = 1 ; i <= N ; ++i) {
cnt[i] -= N - i;
if(cnt[i] <= 0) {puts("0");return;}
S = S * cnt[i] % MOD;
}
for(int i = 1 ; i <= N ; ++i) {
V[i] = (cnt[i] - 1) * fpow(cnt[i],MOD - 2) % MOD;
}
D[0] = 1;
st[0] = 0;
for(int i = 1 ; i <= N ; ++i) {
x[i] = x[i - 1];D[i] = D[i - 1];
if(V[i] == 0) {x[i]++;st[x[i]] = i;ed[x[i] - 1] = i - 1;}
else D[i] = D[i] * V[i] % MOD;
ID[i] = fpow(D[i],MOD - 2);
}
ed[x[N]] = N;
for(int i = 1 ; i <= N ; ++i) {
ans += Query_range(st[x[A[i]]],A[i],D[A[i]] * S % MOD * Inv_2 % MOD);
ans %= MOD;
insert(A[i],ID[A[i]]);
}
memset(tr,0,sizeof(tr));
memset(tr_cnt,0,sizeof(tr_cnt));
for(int i = 1 ; i <= N ; ++i) {
int64 t = Query_range(A[i] + 1,ed[x[A[i]]],ID[A[i]] * S % MOD * Inv_2 % MOD);
ans += ((getnum(N) - getnum(A[i])) * S % MOD + MOD - t) % MOD;
ans %= MOD;
insert(A[i],D[A[i]]);
}
printf("%lld\n",ans);
} int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}

F - 01 on Tree

题解

这个是真的神仙题。。。

脑子里一直是某个按子树大小的贪心,然后一直被叉掉

最后说是初始化每个节点就是一个单独的树,记录这个树里1的个数和0,初始的时候1的个数即为节点值是否为1,0的个数即为节点值是否为0

我们按照\(C_{0i}/C_{1i}\)排序,并把每个点和直接父亲接起来,意思就是把这个点的0和1接在父亲节点的后面,多出来的贡献即是

v是儿子,p是父亲,\(C_{1p} * C_{0v}\)

推一下式子可以证出来选别的儿子肯定不优

然后更新父亲p的值,也就是p变成了一个新的树,这个可以用并查集维护

然后带更新的排序用一个set实现就可以

神仙题神仙题。。。

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <cstring>
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
int N,P[MAXN],V[MAXN],fa[MAXN],C[2][MAXN];
int getfa(int x) {
return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
struct cmp {
bool operator() (const int &a,const int &b) {
if(1LL * C[0][a] * C[1][b] != 1LL * C[0][b] * C[1][a])
return 1LL * C[0][a] * C[1][b] > 1LL * C[0][b] * C[1][a];
else return a > b;
}
};
multiset<int,cmp> S;
void Solve() {
scanf("%d",&N);
for(int i = 2 ; i <= N ; ++i) scanf("%d",&P[i]);
for(int i = 1 ; i <= N ; ++i) scanf("%d",&V[i]);
for(int i = 1 ; i <= N ; ++i) fa[i] = i;
for(int i = 1 ; i <= N ; ++i) C[V[i]][i] = 1;
for(int i = 2 ; i <= N ; ++i) S.insert(i);
int64 ans = 0;
for(int i = 1 ; i <= N - 1 ; ++i) {
int v = *S.begin();
int p = getfa(P[v]);
S.erase(v);S.erase(p);
ans += 1LL * C[0][v] * C[1][p];
C[1][p] += C[1][v];C[0][p] += C[0][v];
fa[v] = p;
if(p != 1) S.insert(p);
}
printf("%lld\n",ans);
} int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}

最新文章

  1. CSS背景background、background-position使用详解
  2. java--HashMap多线程并发问题分析
  3. java时间类简单总结
  4. 为什么没调用 didRegisterForRemoteNotificationsWithDeviceToken 和 didFailToRegisterForRemoteNotificationsWithError
  5. java中日历代码的实现
  6. Ajax调用WebService
  7. ARM菜鸟:JLINK与JTAG的区别
  8. iOS:编译错误Undefined symbols for architecture i386: _OBJC_CLASS_$_XXX&amp;quot;, referenced from: error
  9. idea空包自动叠加问题
  10. Linux知识要点大全(第一章)
  11. 【转载】MySQL5.7 添加用户、删除用户与授权
  12. JMeter 教程汇总链接
  13. 有标号的DAG计数
  14. 25.week4 docker build 也就是创建自己的image 上传image到dockerhub 从dockerhub下载images
  15. centos7安装zabbix3.5
  16. NYOJ 16 矩形嵌套(经典DP)
  17. sgu 126 Boxes
  18. 【文文殿下】Win7打开无线热点
  19. centos验证码图片无法加载的问题
  20. MyBatis笔记——EhCache二级缓存

热门文章

  1. 科学计算三维可视化---TraitsUI(Group对象组织界面)
  2. SpringCloud (十) Hystrix Dashboard单体监控、集群监控、与消息代理结合
  3. Java序员的成长之路
  4. asp.net WebForm程序删除.designer.cs文件之后的故事
  5. linux 自定义yum仓库、repo文件 yum命令
  6. HDU 3535 AreYouBusy (混合背包之分组背包)
  7. SQL SERVER 视图优化经历
  8. 解决Maven并行编译中出现打包错误问题的思路
  9. 【TortoiseSVN】windows中连接SVN服务器的工具
  10. imperva 更改web界面的密码