刷题日记

版权声明:呐,转载请交稿费QAQ[不许转载!] https://blog.csdn.net/zxn0803/article/details/53883421

我先自己立个flagQAQ
如果我一天刷不到5道题/上午刷不到2道题就在第二天的早上/中午跑3圈。
这样就能锻炼身体以及写题辣。
另外,此文章拒绝以任何形式转载,原博客为blog.csdn.net/zxn0803.


由于本人去会考了所以刷题日记停止了一段时间……
这几天会补上之前的题目,争取一天6道以上/在bzoj的day榜上排前三。
加油吧。


2016-12-26

Codeforces Round #383 (Div. 1)
A. Arpa’s loud Owf and Mehrdad’s evil plan
纸张置换题,求个LCM就解决了。
哦……并不是QAQ
然后我们发现它半个环就能解决问题,所以我们在更新的时候要更新半环。
这样求LCM就解决了。

Codeforces Round #372 (Div. 1)
B. Complete The Graph
首先我们可以先让图联通起来。
然后把边一个一个改成1,如果最短路<L那么我们就可以把这个合适地调整到L。这样的话就做完了。

bzoj 1576安全路径
这里写图片描述
并不算很难,但是有些地方好像很麻烦。
首先最短路树唯一,我们可以dij求最短路树。
考虑非树边(u,v),那么在最短路树上有贡献的部分只有(u,v)路径这部分。
其他部分:显然lca以上没有贡献,如果在u的子树和v的子树,那么后续的路径该怎么走还是怎么走,所以不如一开始就走最短路。
看起来只能树剖取最小值,其实不然。
考虑节点x在路径(u,v)上,那么如果节点x要走(u,v)这个路径,那么它的dis就变成:d=dis[u]+dis[v]dis[x]+edge[i].w
其中dis是最短路树的距离。
画画图就知道是对的了。
那么我直接树剖然后把和x无关的打标记就行了。
不想写树剖怎么办?
查询最小值?
实际上把dis[u]dis[v]+edge[i].w进行排序之后,我们能得到一个非树边的数组,那么把这个数组按顺序加进去,然后把这段用并查集缩到lca就行了。
Q:为什么是对的?
A:因为最终答案只取决于最小值,用最小值更新它之后就不用再考虑这个点了。
数组开小了一次……GG

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i; i = edge[i].next)
#define fl edge[i].f
#define vfl edge[i ^ 1].f

using namespace std;
const int N = 200005;
const int M = 200005;
struct Edge{int next,to,w;}edge[M << 1];
struct ed{int a,b,val;}e[M];
int n,m,A[N],B[N],C[N],fa[N],head[N],cnt,dep[N],re[N],dis[N],ent;
bool vis[N]; 
priority_queue<pair<int,int> >q;
void save(int a,int b,int c){edge[cnt] = (Edge){head[a],b,c},head[a] = cnt ++;}
int dfs(int x){if(dep[x])return dep[x];return dep[x] = (dfs(fa[x]) + 1);}
int find(int x,int y,int z)
{
    if(x == y)return x; 
    if(dep[x] < dep[y])swap(x,y);
    if(!re[x])re[x] = z - dis[x];
    fa[x] = find(fa[x],y,z);
}
bool cmp(ed a,ed b){return a.val < b.val;}
#define mp make_pair
#define sec second
void dij()
{
    q.push(mp(0,1));
    while(!q.empty())
    {
        int x = q.top().sec;q.pop();
        if(vis[x])continue;vis[x] = 1;
        RepG(i,x)if(dis[v] > dis[x] + edge[i].w)dis[v] = dis[x] + edge[i].w,q.push(mp(-dis[v],v)),fa[v] = x;
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    Rep(i,m)
    {
        scanf("%d%d%d",&A[i],&B[i],&C[i]);
        save(A[i],B[i],C[i]);
        save(B[i],A[i],C[i]);
    }
    Rep(i,n)dis[i] = 2e9;
    dis[1] = 0;dij();
    dep[1] = 1;rep(i,2,n)dep[i] = dfs(i);
    Rep(i,m)
    {
        if(dis[A[i]] + C[i] == dis[B[i]] || dis[A[i]] - C[i] == dis[B[i]])continue;
        e[++ ent] = (ed){A[i],B[i],dis[A[i]] + dis[B[i]] + C[i]};
    }
    sort(e + 1,e + 1 + ent,cmp);
    Rep(i,ent)find(e[i].a,e[i].b,e[i].val);
    rep(i,2,n)printf("%d\n",re[i] == 0 ? -1 : re[i]);
    return 0;
}

1108: [POI2007]天然气管道Gaz
Description
  Mary试图控制成都的天然气市场。专家已经标示出了最好的天然气井和中转站在成都的地图。现在需要将中转
站和天然气井连接起来。每个中转站必须被连接到正好一个钻油井,反之亦然。 Mary特别指名,建设的天然气管
道必须从某个天然气井开始,向南或者向东建设。Mary想知道怎么连接每个天然气井和中转站,使得需要的天然气
管道的总长度最小。
Input
  第一行为一个正整数n(2<=n<=50000),表示天然气井的数量(中转站的数量与之相等)。接下来n行,每行两
个整数xi和yi(0<=xi,yi<=100000),表示天然气井的坐标。向东走则x坐标增加,向北走则y坐标增加。接下来n
行,每行两个数xj’和yj’(0<=xj’,yj’<=100000),表示中转站的坐标。
Output
  第一行包含一个数,表示最短的连接管道长度。
orzfsf!实际上把式子写出来直接算就行了。

#include <cstdio>
int n;
int main() {
    scanf("%d", &n);
    long long ans = 0;
    int x, y;
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d", &x, &y);
        ans += y - x;
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d", &x, &y);
        ans += x - y;
    }
    printf("%lld\n", ans);
    return 0;
}

1115: [POI2009]石子游戏Kam
Description
有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数。两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏。问先手是否必胜。
Input
第一行u表示数据组数。对于每组数据,第一行N表示石子堆数,第二行N个数ai表示第i堆石子的个数(a1<=a2<=……<=an)。 1<=u<=10 1<=n<=1000 0<=ai<=10000
Output
u行,若先手必胜输出TAK,否则输出NIE。
差分后阶梯Nim。
注意阶梯是向后推的……
被坑了QAQ

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i; i = edge[i].next)
#define fl edge[i].f
#define vfl edge[i ^ 1].f

using namespace std;
const int N = 1005;
int n,m,a[N],b[N];
int main()
{
    scanf("%d",&m);
    while(m --)
    {
        scanf("%d",&n);
        int re = 0;
        Rep(i,n)scanf("%d",&a[i]);
        b[1] = a[1];
        rep(i,2,n)b[i] = a[i] - a[i - 1];
        for(int i = n;i > 0;i -= 2)re ^= b[i];
        puts(!re ? "NIE" : "TAK");
    }
    return 0;
}

2016-12-17

bzoj 1150: [CTSC2007]数据备份Backup
首先转化成线段的模型。
变成:有n个点n-1条边,相邻的边不能同时选,求强制选k条边的最小的边权和(边权为相邻的点的坐标差)。
我们发现这个就是增广,//但是我并不会这种高贵的做法。
所以我们考虑反悔序列
假如我们相邻的三个x,y,z.我知道y现在是最小的,那么我先选上y。
然后x,z都不能选了,那怎么办呢?
我们不选y转而选x,z的想法就产生了。
把val[x] + val[z] - val[y]加入到堆里,作为反悔序列。
把y的权值设置为val[x] + val[z] - val[y],同时直接删掉x和z。
注意一个问题:
Q:删了x和z,然而堆里还有一些东西的next指针为x(pre == z)?
A:在删除的时候把全局的结构体改一改,在弹堆顶的时候,把那个位置的元素取出来,而不用当前在堆里的元素,这样指针部分就不会出问题了。
这样正确性是显然的,每次相当于增广1的流量。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
typedef long long LL;
const LL inf = 1ll << 60;
const int N = 100005;
LL ans;
bool del[N];
struct Node
{
    LL val;
    int next,pre,id;
}t[N];
bool operator < (Node A,Node B){return A.val > B.val;}
priority_queue<Node>q;
void Del(Node &x) //更新为反悔节点 
{
    del[x.pre] = del[x.next] = 1;
    x.val = min(inf,t[x.pre].val + t[x.next].val - x.val);
    x.pre = t[x.pre].pre;
    t[x.pre].next = x.id;
    x.next = t[x.next].next;
    t[x.next].pre = x.id;//更新全局数组
    t[x.id] = x;
}
int last,now,n,m;
int main()
{
    scanf("%d%d",&n,&m);
    Rep(i,n)
    {
        scanf("%d",&now);
        t[i].val = now - last;
        t[i].pre = i - 1;
        t[i].next = i + 1;
        t[i].id = i;
        last = now;
    }
    t[1].val = inf,t[n + 1].val = inf,t[n + 1].id = n + 1,t[n + 1].pre = n;
    Rep(i,n + 1)q.push(t[i]);
    while(m --)
    {
        while(del[q.top().id])q.pop();
        Node x = q.top();
        q.pop();
        //这时候需要把x变成t[x],因为它的next和pre指针变了。 
        x = t[x.id];
        ans += x.val;
        Del(x);
        q.push(x);
    }
    printf("%lld\n",ans);
    return 0;
}

bzoj 2563: 阿狸和桃子的游戏
集训的时候讲的题,眼熟然后秒了QAQ
边权肯定不好做,但是只有差的话,我们可以把边权转成点权,把之前的点权*2,按照点权排序之后,把差除以二就行了。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
typedef long long LL;
const int N = 20005;
LL val[N];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    Rep(i,n)scanf("%lld",&val[i]),val[i] *= 2;
    Rep(i,m)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        val[a] += c,val[b] += c;
    }
    sort(val + 1,val + 1 + n);
    LL a = 0,b = 0;
    Rep(i,n)
    {
        if(i & 1)a += val[i];
        else b += val[i];
    }
    printf("%lld\n",(b - a) / 2);
    return 0;
}

中午不用跑步了QAQ开心
bzoj 1116: [POI2008]CLO

Description
Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 你要把其中一些road变成单向边使得:每个town都有且只有一个入度(双向边入度不计算)
Input
第一行输入n m.1 <= n<= 100000,1 <= m <= 200000 下面M行用于描述M条边.
Output
TAK或者NIE 常做POI的同学,应该知道这两个单词的了…
首先,每个点只有1个入度,那么显然的只有环套树能满足要求。
然后再想想的话,我们用个并查集维护环套树就行了,在查找到环的时候,在根的地方打一个有环的标记就行了。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
const int N = 200005;
int fa[N],n,m;
bool tag[N];
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
int main()
{
    scanf("%d%d",&n,&m);
    Rep(i,n)fa[i] = i;
    Rep(i,m)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(find(a) == find(b))
            tag[find(a)] = 1;
        else 
        {
            int tx = find(a),ty = find(b);
            if(tag[tx])tag[ty] = 1;
            fa[tx] = ty;
        }
    }
    bool ans = 1;
    Rep(i,n)ans &= tag[find(i)];
    puts(ans ? "TAK" : "NIE");
    return 0;
}

51Nod 1577
L,R之间是不是能选出一些数字异或起来等于K.
题解:被逗了……
我们不能用平常的方法(ST+线性基暴力合并),因为这样做稳T。
那怎么做呢。
考虑记录后缀线性基,我们可以很好的维护后缀线性基[增量法],但是这有什么用呢?
如果我们在有后缀线性基的同时满足这组线性基的下标尽可能小,那么到以L开头的后缀线性基里面跑一下,看能不能跑出K来就行了。
现在问题是这样的线性基我们怎么建。
建立方法:
考虑一个数a[i],如果上来第一位就没有,直接加进去。
如果有这一位,那么我们用下标小的去替换这一位,然后把另外的一个拿下来,和当前的这位异或一下,继续跑。
这样的话,如果我们加入的a[i]是个可以被线性表出的值,我们是可以对线性基整体进行更新的。
之后就随便跑了。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
int read()
{
    int ans = 0;char ch = getchar();
    while(ch > '9' || ch < '0')ch = getchar();
    while(ch >= '0' && ch <= '9')ans = ans * 10 + ch - '0',ch = getchar();
    return ans;
}
const int N = 100005;
int n,m,a[N];
struct Node
{
    int val[30],id[30];
    int &operator [](int x){return val[x];}
    void ins(int x,int y)
    {
        for(int i = 29;~ i;-- i)
        {
            if(x & (1 << i) && val[i])
            {
                if(id[i] > y)swap(x,val[i]),swap(y,id[i]);
                x ^= val[i];
            }
            else if(x & (1 << i)){val[i] = x;id[i] = y;break;}
        }
    }
    bool check(int x,int R)
    {
        int mx = 0;
        for(int i = 29;~ i;-- i)
            if(x & (1 << i))x ^= val[i],mx = max(mx,id[i]);
        return (!x) && mx <= R;
    }
}f[N];
int main()
{
    n = read();
    Rep(i,n)a[i] = read();
    Dwn(i,n)
    {
        f[i] = f[i + 1];
        f[i].ins(a[i],i);
    }
    m = read();
    Rep(i,m)
    {
        int L,R,K;
        L = read(),R = read(),K = read();
        puts(f[L].check(K,R) ? "YES" : "NO");
    }
    return 0;
}

Div1 278 C. Prefix Product Sequence
注意i/(i1)的取值不同,那直接做。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i; i = edge[i].next)
#define fl edge[i].f
#define vfl edge[i ^ 1].f

using namespace std;
int n,m;
int vis[100005];
int rev(int x,int p){int tmp = 1;while(p){if(p & 1)tmp = 1ll * tmp * x % n;p >>= 1;x = 1ll * x * x % n;}return tmp;}
int main()
{
    scanf("%d",&n);
    int cur = 1;
    if(n != 1 && n != 4)for(int i = 2;i * i <= n;++ i)if(n % i == 0)return puts("NO"),0;
    puts("YES");
    if(n == 1)puts("1");
    else if(n == 4)printf("1\n3\n2\n4\n");
    else 
    {
        puts("1");
        for(int i = 2;i < n;++ i)
            printf("%d\n",1ll * i * rev(i - 1,n - 2) % n);
        printf("%d\n",n);
    }
    return 0;
}

中间隔了好几天……
这几天基本什么事情都没做,一直在看会考题,预计刷题日记在自己AK一次会考题之后继续开始。
不过期间把CTSC的那个网络管理顺手切了。
45min不到就写完了……然后就去复习会考去了QAQ
物理老师:”跟你说,如果你不想着拿A应该还是没啥问题的。”
我:“我要!这铁棒有何用……?”

然后zxn就睡不着觉了QAQ
然后在1/3的凌晨2:40的时候完成了一个SB题……
推式子推对了写前缀和写错了……
我是个SB……
2901: 矩阵求和
给出两个n*n的矩阵,m次询问它们的积中给定子矩阵的数值和。
mn可过。

i=acj=bdk=1na[i][k]b[k][j]

看起来没法优化么。
怎么可能……
乘法分配律我学了一辈子都没学会。
提取a再提取b,然后前缀和。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i; i = edge[i].next)
#define fl edge[i].f
#define vfl edge[i ^ 1].f

using namespace std;
int n,m;
const int N = 2005;
typedef long long LL;
int read()
{
    int x = 0;
    char ch = getchar();
    while(ch > '9' || ch < '0')ch = getchar();
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar();
    return x;
}
LL a[N][N],b[N][N];
LL solve(int A,int B,int C,int D)
{
    if(B > D)swap(A,C),swap(B,D);
    if(A > C)
    {
        int ta = A,tb = B,tc = C,td = D;
        A = tc,B = B,C = ta,D = td;
    }
    LL re = 0;
    Rep(k,n)re += (a[C][k] - a[A - 1][k]) * (b[k][D] - b[k][B - 1]);
    return re;
}
int main()
{
    scanf("%d%d",&n,&m);
    Rep(i,n)Rep(j,n)a[i][j] = read();
    Rep(i,n)Rep(j,n)b[i][j] = read();
    Rep(i,n)Rep(j,n)a[i][j] += a[i - 1][j];
    Rep(i,n)Rep(j,n)b[i][j] += b[i][j - 1];
    while(m --)
    {
        int A,B,C,D;
        A = read(),B = read(),C = read(),D = read();
        printf("%lld\n",solve(A,B,C,D));
    }
    return 0;
}

一些物理笔记乱入
1.先考虑功能关系再考虑受力分析,这样不容易忘记动能定理的使用。
2.系统牛顿第二定律的使用。
一些数学笔记乱入
1.辅助角公式挺重要的。
2.认真带入诱导公式……不然符号会挂。
3.在没有给出两点坐标的时候,坐标记得可交换。
4.正弦定理也很有用。
5.不要乱算截止时间啊!!!
对于一个木板上的小滑块,考虑它的与木板速度相同的时刻,这要解方程才能算!


会考结束了QAQ
接下来就是每天刷五道题了QAQ
//至于刀剑那个番就不补了……有生之年系列QAQ


2017/1/8

调整了一下状态。。。
晚上看了一小会直播然后被婊了。。。。
然后去写一个SB题,题目来源是
bzoj3439: Kpm的MC密码

Description

背景

想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的。。。),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了。。。

描述

Kpm当年设下的问题是这样的:

现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串。

系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每一个ki,要求你求出列出的n个字符串中所有是si的kpm串的字符串的编号中第ki小的数,如果不存在第ki小的数,则用-1代替。(比如说给出的字符串是cd,abcd,bcd,此时k1=2,那么”cd”的kpm串有”cd”,”abcd”,”bcd”,编号分别为1,2,3其中第2小的编号就是2)(PS:如果你能在相当快的时间里回答完所有n个ki的查询,那么你就可以成功帮kpm进入MC啦~~)

Input

第一行一个整数 n 表示字符串的数目

接下来第二行到n+1行总共n行,每行包括一个字符串,第i+1行的字符串表示编号为i的字符串

接下来包括n行,每行包括一个整数ki,意义如上题所示

Output

包括n行,第i行包括一个整数,表示所有是si的kpm串的字符串的编号中第ki小的数

首先想到了Trie树,这是显然的。
然后想到,欸我们可以二分答案,这也是显然的。
那么我们可以二分答案+在可持久化Trie上进行查询。
复杂度是nlog2n,但是自己当时美滋滋,并没有注意到。
考虑了一下之后,我们可以用dfs序,因为是子树。
那么dfs一遍这个Trie树之后,就可以用主席树来维护它了。
这波就稳了,但是似乎还是出了点毛病,这是因为我人傻逼。
用dfs序建立主席树的时候:
vec[x].size()
话说莫非不是vec[Rank[x]]么……

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;

#define u t[x]
#define o t[y]
#define mid (l + r >> 1)
#define lson u.lc,l,mid,o.lc
#define rson u.rc,mid + 1,r,o.rc
const int N = 300005;
struct Tree{int cnt,lc,rc;}t[N * 26];
int n,m,ch[N][26],id[N],insv,tot = 1,stot,ql,qr,st[N],ed[N],tim,qk,rt[N],oo,Rank[N];
vector<int>vec[N];
#define v ch[x][c]
void ins(char *s,int z)
{
    int x = 1,len = strlen(s + 1);
    for(int i = len;i;-- i)
    {
        int c = s[i] - 'a';
        if(!v)v = ++ tot;
        x = v;
    }
    id[z] = x;
    vec[x].push_back(z);
}
void dfs(int x){Rank[st[x] = ++ tim] = x;for(int c = 0;c < 26;++ c)if(v)dfs(v);ed[x] = tim; }//printf("!dfs %d %d %d\n",x,st[x],ed[x]);}
void ins(int &x,int l,int r,int y)
{
    t[x = ++ stot] = o;
    u.cnt ++;
    if(l == r)return;
    if(insv <= mid)ins(lson);
    else ins(rson);
}
int Qry(int x,int l,int r,int y)
{
    if(l == r)return l;
    int z = t[o.lc].cnt - t[u.lc].cnt;
    if(z >= qk)return Qry(lson);
    else return qk -= z,Qry(rson);
}
void Qry(int S,int T,int i)
{
    ql = S - 1,qr = T,qk = i;
    if(t[rt[qr]].cnt - t[rt[ql]].cnt < qk)puts("-1");
    else printf("%d\n",Qry(rt[ql],1,n,rt[qr]));
}
char s[N];
int main()
{
    scanf("%d",&n);
    Rep(i,n)scanf("%s",s + 1),ins(s,i);
    dfs(1);
    rt[0] = ++ stot;
    Rep(x,tot)
    {
        int z = rt[x - 1];
        if(vec[Rank[x]].size() != 0)
        {
            for(vector<int> :: iterator it = vec[Rank[x]].begin();it != vec[Rank[x]].end();++ it)
            {
                insv = *it;
                ins(rt[x],1,n,z);
                z = rt[x];
            }   
        }
        else rt[x] = rt[x - 1];
    }
    Rep(i,n){int k;scanf("%d",&k);Qry(st[id[i]],ed[id[i]],k);}
    return 0;
}

快乐咸鱼每一天。

2017/1/9

1.bzoj2849: 和谐串
其实自己水了一题……
打表发现极速收敛,想了想决定写个大暴力剩下随便输出。
然后发现SPJ挂了(喜闻乐见)ydsy.youhaovip.com/
2.bzoj2400: Spoj 839 Optimal Marks
经典题目QAQ
最小割是显然的,但是第二问怎么处理呢?
我们可以用进制思想,那么在优先满足第一问的情况下满足第二问……
代价*k就行了对吧。
代码是fsf当时写的,感觉没什么代码实现难度就没有写。

#include <cstdio>
#include <algorithm>
using namespace std;

#define PR 1010

typedef long long i64;

#define iter(i, n) for (int i = 1; i <= n; ++i)
#define iter0(i, n) for (int i = 0; i < n; ++i)
#define iter_e(i, x) for (int i = pos[x]; i; i = g[i].frt)

struct Edge { int y, frt, f; } g[101000];
int pos[PR], lv[PR], q[PR], w[PR], ex[2010], ey[2010];
int gsz, n, m, S, T;
i64 graph_v, sum_v;

void add_edge(int x, int y, int w0, int w1 = 0) {
    g[++gsz] = (Edge) { y, pos[x], w0 }, pos[x] = gsz;
    g[++gsz] = (Edge) { x, pos[y], w1 }, pos[y] = gsz;
}

#define v g[i].y
#define fl g[i].f
#define vfl g[i ^ 1].f

const int inf = 1e9;
const int Z = 1000;

bool denote() {
    int h, t, x;
    iter(i, T) lv[i] = -1;
    lv[q[h = t = 1] = S] = 0;
    while (h <= t) {
        x = q[h++];
        iter_e(i, x) if (fl > 0 && lv[v] < 0) lv[q[++t] = v] = lv[x] + 1;
    }
    return lv[T] > 0;
}

int augment(int x, int f) {
    if (x == T) return f;
    int ff, s = 0;
    iter_e(i, x) if (fl > 0 && f > 0 && lv[v] == lv[x] + 1 && (ff = augment(v, min(f, fl))))
        fl -= ff, vfl += ff, f -= ff, s += ff;
    if (s == 0) lv[x] = -1;
    return s;
}

int max_flow() { int s = 0; for (; denote(); s += augment(S, inf)); return s; }



void build(int d) {
    gsz = 1;
    iter(i, T) pos[i] = 0;

    //S 0
    //T 1

    iter(i, n) {
        if (w[i] >= 0) {
            if (w[i] >> d & 1) add_edge(i, T, inf), sum_v += (1 << d);
            else add_edge(S, i, inf);
        } else {
            add_edge(S, i, 1);
        }
    }

    iter(i, m) {
        add_edge(ex[i], ey[i], Z, Z);
    }

    int flow = max_flow();

    graph_v += (i64) (flow / Z) << d;
    sum_v += (i64) (flow % Z) << d;
}

int main() {
    scanf("%d%d", &n, &m);
    S = n + 1, T = n + 2;
    iter(i, n) {
        scanf("%d", &w[i]);
    }
    iter(i, m) scanf("%d%d", &ex[i], &ey[i]);
    iter0(i, 31) {
        build(i);
    }
    printf("%lld\n%lld\n", graph_v, sum_v);
    return 0;
}

3.bzoj3498: PA2009 Cakes
忽然咸鱼。
我们有一个结论是说,三元环的个数为msqrt(m)个,那么我们把边按照度数进行排序,然后把度数小的点向度数大的点连边,用哈希查询连通性。
但是TMD我怎么就过不去啊摔!
Claris有一个更厉害的做法,感觉复杂度是差不多的啊。
以下是Claris的做法。
不妨每个点只向小于它标号的点连边。
枚举每个点x,并枚举它的后继v,然后考虑v的度数。
如果v的度数小于根号m,那么枚举v的每个后继vv,判断xvv是否联通。
否则枚举x的其他后继vv,看vvv是否联通。
然后我就咸鱼了……并不知道为啥自己的那么慢。

4.bzoj2705: [SDOI2012]Longge的问题
被婊了之后心情挺不好……
每天墨迹着同样一件事的感觉真的是不怎么样……
考虑枚举约数k,那么实际上就求与n/k互素的数的个数,欧拉函数随便算。

5.bzoj1411: [ZJOI2009]硬币游戏
在dalao们的提醒下,自己终于注意到了一些事情。
注意到,如果恰好是2k步,那么i只与i+2ki2k有关,理由是这样的:
考虑一步一步手动展开,发现1和2满足这个规律。
我们可以归纳法啊。
如果y.......1.....x......1........y是满足2k的,即x这个位置只和1所在的位置有关。
那么考虑下2k的操作,那些1的位置会和xy有关。
那么实际上2k+1只和y的位置有关,证毕。
那么就是:对T进行二进制分解,先做2k,然后保存一下这个数组,再做下一个2k
总复杂度是nlogn.

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i; i = edge[i].next)
#define fl edge[i].f
#define vfl edge[i ^ 1].f

using namespace std;

typedef long long LL;
const int N = 200005;
LL T;
int a[N],b[N],n,m;
int cal(LL x,LL y)
{
    LL L = ((x - y) % m + m) % m;
    LL R = (x + y) % m;
    if(a[L] == 0)return 0;
    if(a[L] == a[R])return 1;
    return 2;
}
void doit(LL T,LL len)
{
    if(!T)return ;
    if((T & 1))
    {
        for(int i = 0;i < m;++ i)b[i] = cal(i,len);
        for(int i = 0;i < m;++ i)a[i] = b[i];
    }
    doit(T >> 1,len << 1);
}
int main()
{
    scanf("%d%lld",&n,&T);
    for(int i = 1;i <= n;++ i)scanf("%d",&a[2 * i - 2]);
    m = n << 1;
    doit(T,1);
    for(int i = 0;i < m;++ i)printf("%d%c",a[i],i == m - 1 ? '\n' : ' ');
    return 0;
}

2017/1/10

1.bzoj 2142: 礼物

n!(na1)!(na1)!(na1a2)!..

对阶乘进行素数分解,可以看作有p的倍数和没有p的倍数。
预处理之后,可以求出(pk1)!modpk的值r
n!是有循环节的,ans[1,n]=rnpk(nmodpk)!modpk
然后CRT合并即可。

大哥我错了……
我再也不嘴巴题解了……
不知道为啥这样做崩掉了……
正常向的做法是这样的,与上述思路相仿,我们考虑一个函数solve(n,p)表示n!
可以被表示成n!=spusu的值。
那么预处理一个fac数组,fac[i](i < pk)表示在<pk的里面,去掉含有p的因子的阶乘。如果p2,那么fac[7]=1357
得到这个之后,我们考虑对数进行分类。
p0t,p1t,…
注意到在提取出一个p之后,就变成了(123...n/p)p+123....
右边那个加号显然是fac[pk1](n/p)+frc[n%pk].
左边那个就是solve(n/p,p)p,容易发现复杂度是log的。
那么这个题就做完了,现在我想知道自己原来的思路错在哪里……。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
#define mp make_pair
#define X first
#define Y second
vector<pair<int,int> > vec;
int up[200005][105],fac[200005][105],len[105],n,ans[105],m;
typedef long long LL;
LL res[105][2];
LL P,w[105];
void mul(int &x,int y,int z){x = 1ll * x * y % z;}
void init()
{
    for(int i = 2;i <= 100000;++ i)
    {
        if(P % i == 0)
        {
            int re = 0;
            while(P % i == 0)P /= i,re ++;
            vec.push_back(mp(i,re));
        }
    }
    int tt = 0;
    for(vector<pair<int,int> > :: iterator it = vec.begin();it != vec.end();++ it)
    {
        int p = (*it).X,z = (*it).Y,re = 1;
        Rep(i,z)re *= p;
        fac[0][++ tt] = 1;fac[1][tt] = 1;
        up[0][tt] = 0,up[1][tt] = 0;len[tt] = re;
        for(int i = 2;i <= re;++ i)
        {
            up[i][tt] = up[i - 1][tt];
            fac[i][tt] = fac[i - 1][tt];
            int y = i,zz = 0;
            while(y % p == 0)zz ++,y /= p;
            if(!zz)mul(fac[i][tt],y,re);
            up[i][tt] += zz;
        }
    }
}
int Pw(int x,int y,int p){int re = 1;while(y){if(y & 1)re = 1ll * re * x % p;y >>= 1;x = 1ll * x * x % p;}return re;}
int rev(int x,int y,int z) // x % y意义下的逆元 ,y = z ^ k
{int p = y;y = y / z * (z - 1);return Pw(x,y - 1,p);}
void exgcd(LL a,LL b,LL &x,LL &y){b ? (exgcd(b,a % b,y,x),y -= a / b * x) : (x = 1,y = 0);}
LL CRT(int *r,int *p)
{
    LL R,M,k1,k2;
    R = r[1],M = p[1];
    int zzz = vec.size();
    for(int i = 2;i <= zzz;++ i)
    {
        LL dt = r[i] - R,d = __gcd((LL)p[i],M);
        if(dt % d)while(1);
        exgcd(M / d,p[i] / d,k1,k2);
        k1 = (k1 * dt / d) % (p[i] / d);
        R = R + k1 * M;
        M = M / d * p[i];
        R %= M;
    }
    return R < 0 ? R + M : R;
}
LL work(int t,int i,int p,int pz)
{
    if(t < p)return fac[t][i];
    return 1ll * fac[t % pz][i] * Pw(fac[pz - 1][i],t / pz,pz) % pz * work(t / p,i,p,pz) % pz;
}
LL f(int id,int p,int pz){LL re = 1;Rep(i,m)re = re * work(w[i],id,p,pz) % pz;return re;}
LL getP(LL a,int p){LL t = a / p;if(t)t += getP(t,p);return t;}
LL g(int p){LL re = 0;Rep(i,m)re += getP(w[i],p);return re;}
void calc()
{
    int i = 0;
    for(vector<pair<int,int> > :: iterator it = vec.begin();it != vec.end();++ it)
    {
        ++ i;int p = (*it).X,z = (*it).Y,re = 1,pz = len[i];
        res[i][0] = work(n,i,p,pz) % pz;
        res[i][0] = 1ll * res[i][0] * rev(f(i,p,pz),pz,p) % pz;
        res[i][1] = getP(n,p) - g(p);
        int ls = 1;
        if(res[i][1] >= z)ls = 0;
        else for(int j = 1;j <= res[i][1];++ j)ls = ls * p;
        ans[i] = res[i][0] * ls % len[i];
    }
}
int main()
{
    scanf("%lld%d%d",&P,&n,&m);
    LL sss = 0;
    Rep(i,m)scanf("%lld",&w[i]),sss += w[i];
    if(sss > n)return puts("Impossible"),0;
    else w[++ m] = n - sss;
    init();
    calc();
    printf("%lld\n",CRT(ans,len));
    return 0;
}

2.Codeforces Round #174 (Div. 1)A
题意:有三种操作,1.[1,ai]区间+k.2.push_back(val).3.pop_back()
题解:其实就可以直接在那个下标上打标记,在没删它之前都没啥事情,删它的时候把标记丢前面去就行了。然而我SB写了log的。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
typedef long long LL;
const int N = 200005;
LL t[N];
int val[N],n;
void Add(int x,int y){for(;x;x -= x & -x)t[x] += y;}
LL Qry(int x){LL z = 0;for(;x <= 200000;x += x & -x)z += t[x];return z;}
int main()
{
    scanf("%d",&n);
    int sz = 1;LL sum = 0;
    while(n --)
    {
        int op;
        scanf("%d",&op);
        if(op == 1)
        {
            int x,y;
            scanf("%d%d",&x,&y);Add(x,y);
            sum += 1ll * x * y;
        }
        else if(op == 2)
        {
            int x;
            scanf("%d",&x);
            val[++ sz] = x;
            val[sz] -= Qry(sz);
            sum += x;
        }
        else {sum -= val[sz];val[sz] = 0;sum -= Qry(sz --);}
        printf("%.7f\n",1.0 * sum / sz);
    }
    return 0;
}

3.Codeforces174 B.
题意是给你个数列a2..an,把a11开始循环到n1.
每次循环的时候,如果是奇数次循环,那么x+=a[x],y+=a[y].否则x=a[x],y+=a[y].
容易发现,它有可能数组越界,也有可能卡死在里面。
如果越界就直接循环结束输出y。
如果卡死在里面输出-1.

题解:
维护一个跳转的数组trans[x][2],然后看那些点是不是在环上就行了,反正我是一条咸鱼。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
const int N = 200005;
typedef long long LL;
int to[N][2],val[N],tid[N][2],tim,n;
bool ban[N][2],sur[N][2],g[N][2];
LL tr[N][2];
bool dfs(int x,int d)
{
//  printf("~~ %d %d %d %d\n",x,d,tid[x][d],tim);
    if(x > n || x < 1)return 0;
    if(sur[x][d])return ban[x][d];
    if(tid[x][d] != tim)tid[x][d] = tim;
    else return ban[x][d] = 1;
    ban[x][d] = dfs(to[x][d],d ^ 1);
    sur[x][d] = 1;
    return ban[x][d];
}
LL Find(int x,int d)
{
    if(x > n || x < 1)return 0;
    if(!g[x][d]){g[x][d] = 1;return tr[x][d] = Find(to[x][d],d ^ 1) + val[x];}
    else return tr[x][d];
}
int main()
{
    scanf("%d",&n);
    sur[1][1] = ban[1][1] = 1;
    sur[1][0] = 1,ban[1][0] = 0;
    Rep(i,n - 1)scanf("%d",&val[i + 1]),to[i + 1][0] = i + 1 - val[i + 1],to[i + 1][1] = i + 1 + val[i + 1];
    for(int i = 2;i <= n;++ i)
    {

        int x = i,d = 0;
        ++ tim;
        if(!sur[x][d])ban[x][d] = dfs(x,d);
        if(!g[x][d] && !ban[x][d])tr[x][d] = Find(x,d);
        d ^= 1;
        ++ tim;
        if(!sur[x][d])ban[x][d] = dfs(x,d);
        if(!g[x][d] && !ban[x][d])Find(x,d);
        //printf("Now %d\n",i);
    }
    Rep(i,n - 1)
    {
        if(ban[i + 1][0])puts("-1");
        else printf("%lld\n",tr[i + 1][0] + i);
    }
    return 0;
}

4.Codeforces Round #174 (Div. 1)C
题意是给你n种硬币,每种都有个价值,然后告诉你说对于每个i,都有限制xi小于yi,即第xi种硬币的数目要小于yi的数目。
保证x集合的数两两不同,y集合中的也是。
求正好凑到T的方案数。
题解:
这TM……
把father边建出来,然后dfs,然后DP.
5.Codeforces Round #174 (Div. 1)D
题意:给个序列an,让你改一改这个序列,满足对于任意i都能有ai=ai+1,求最少需要改多少个。
题解:
奇数权值不难想,关键是偶数咋办。
展开一下式子,12a2n+2anv12an=an1
然后这样就意味着相邻的要在模an意义下为12an.
复杂度是n2


2017-1-11

1.Codeforces Round #174 (Div. 1)E
题意:给n个不同权值,每个权值代表第i个人的能力,保证能力大的会战胜能力小的。有m次操作,每次翻转权值[a,b]的胜负性。现在问三元环的个数。
题解:我们考虑计算这样的问题,就是一个完全图,边为白色,有一些边为黑色边,求异色三元环的个数。
考虑每个点i,它的黑色的入度是d[i],那么异色三元环个数有:

ans=ind[i](nd[i]1)

但是仔细想想发现不太对,因为考虑黑白黑的三元环,它被算了2次,而白黑白的三元环也被算了2次(画图可以知道)
ans=nid[i](nd[i]1)2

考虑原来的序列,相当于每个点都向其他点引了一条白边。
而翻转一段权值[a,b]相当于把一个点i[a,b]到这段区间的边改成黑色的。
最后求异色三元环个数。
二维线段树解决就行了。
Q:你是不是脑残!
A:是的!
扫描线显然是可行的对吧。
2.bzoj4209: 西瓜王
题意:给个序列,每次问一段区间内的强制选出k对数的最大和等于多少。其中这k对数要求每对奇偶性都相同。
题解:
你就开个主席树然后记录一下就行了。
3.Codeforces#385Div1A
题意:n个点m条边的简单无向图。有k个点是特殊点,你可以保证在简单无向图的条件下任意连边,问这k个点不联通的前提下,最多能加多少边。
题解:并查集,然后把初始的边减掉。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
const int N = 1005;
int n,m,k,fa[N],sz[N],ans[N],f[N];
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
bool sp[N];
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    Rep(i,k){int x;scanf("%d",&x);sp[x] = 1;}
    Rep(i,n)fa[i] = i,sz[i] = 1;
    Rep(i,m)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(find(a) != find(b))
        {
            sz[find(b)] += sz[find(a)];
            fa[find(a)] = find(b);
        }
    }
    long long an = 0;
    //Rep(i,n)ans += 1ll * sz[i] * (sz[i] - 1) / 2;
    int re = 0;
    Rep(i,n)if(sp[i])re = max(re,sz[find(i)]);
    Rep(i,n)
    {
        if(fa[i] != i)continue;
        Rep(j,n)
        {
            if(sp[j] && find(j) == find(i))
            {
                f[i] = j;
                break;
            }
        }
    }
    Rep(i,n)if(sp[i])ans[i] = sz[find(i)];
    Rep(i,n)
    {
        if(fa[i] == i && !f[i])
        {
            int id = 0,mx = 0;
            Rep(j,n)
                if(mx < ans[j])mx = ans[j],id = j;
            ans[id] += sz[i];
        }
    }
    Rep(i,n)if(ans[i])an += 1ll * ans[i] * (ans[i] - 1) / 2;
    an -= m;
    printf("%I64d\n",an);
    return 0;
}

4.Codeforces#385Div1B
题意:给你个nn的矩阵,对角线权值为0,你可以问不超过20个问题,每次问的问题的格式是一个长度为k的排列(k的长度由你自己指定){ai}
每次问的时候会给你返回每一行的对应这k个位置上的所有的数字的最小值,求最后每行的最小值是什么。
题解:
考虑对每一行i都进行二进制分解,我们可以认为第i行的最小值实际上就是所有的p满足p and i==0的这些权值

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
const int N = 1005;
int n,mn[N];
void solve(int x,int y)
{
    if((1 << x) > n)return ;
    int cur = 0;
    Rep(i,n)if(((i & (1 << x)) >> x) == y)cur ++;
    printf("%d\n",cur);fflush(stdout);
    Rep(i,n)if(((i & (1 << x)) >> x) == y)cur --,printf("%d%c",i,cur == 0 ? '\n' : ' '),fflush(stdout);
    Rep(i,n){int z;scanf("%d",&z);if(((i & (1 << x)) >> x) != y)mn[i] = min(mn[i],z);}
    if(!y)solve(x,y ^ 1);
    else solve(x + 1,0);
}
int main()
{
    scanf("%d",&n);
    Rep(i,n)mn[i] = (int)1e9 + 1;
    solve(0,0);
    puts("-1");fflush(stdout);
    Rep(i,n)printf("%d%c",mn[i],i == n ? '\n' : ' ');
    return 0;
}

2017-1-12

1.bzoj1977: [BeiJing2010组队]次小生成树 Tree
有图所以不贴了。
题解:
首先猜个结论就是我们只会替换掉一条边。
然后去百度一下题解发现是对的。
那么就是枚举非树边然后树剖就行了吧。
但是注意这是边树剖,所以写起来还是很蛋疼。
随手一写就写完了。
2.bzoj4710: [Jsoi2011]分特产
肯定可以用组合数算,然后没想那么多,发现自己读错题了。
回过头来看肯定还是用组合数算,然后发现就不知道怎么算不合法的。
到底会不会容斥啊……
那就是我们能算出至多满足i个人的情况,现在问题在于怎么算强制满足i个人的情况。
显然的事情就是二项式反演(容斥)就行了,相当于f(n)=(1)k(nk)g(k)
我这里似乎要奇加偶减。
但是不知道哪里莫名其妙挂了。
我也很绝望啊。
不绝望了……
-1的那个指数写错了……
3.Codecraft-17 and Codeforces Round #391 (Div. 1 + Div. 2, combined)C
题意:
有n个vector,每个里面有一些元素。
现在让你所有置换,使得求每个元素在经过这个置换之后,在每个vector里面的出现次数不变。
题解:
我TMD考场上都想到最后一步了就是不会用MAP?
首先肯定是度数相同的呆在一起,再直接了当的点说,就是把这个东西变成字符串的形式。
每个东西的形式是:次数+位置。
我们按照这个字符串哈希一下,然后把相同的字符串用阶乘一算就行了。
好了,问题来了。
这其实就是map搞一下就没了对吧。
直接每个元素的字符串变成出现位置重复i次,拼在一起,然后直接map扫一遍。
我真的是个脑残。
明天可以早上起床跑圈了……

2017/1/14

1.bzoj2339:卡农
题意:定义一个合法的01串集为:一共有M个串,每个串长为N,每
一位恰有偶数个1。问有多少个合法的01串集。
题解:
肯定我们发现可以补集转化一波。
f[i]表示强制到i是合法的方案数。
那么有f[i]=A(2N,i)fi1fi2(i1)(2N2i)
解释的话,第一个减号是因为不能加入空串(题目规定)。
第二个减号:有重复的,那么就是f[i2]乘上重复的方案。
重复的排列方案显然是(排列:)(i - 1)乘上和之前(i - 2)个数字任何一个重复就行了。
然后就做完了。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)

using namespace std;
const int N = 1e6 + 5;
int n,m,f[N],fac[N],A[N];
const int mod = 1e8 + 7;
int power(int a,int p,int pc){int tmp = 1;while(p){if(p & 1)tmp = 1ll * a * tmp % pc;p >>= 1;a = 1ll * a * a % pc;}return tmp;}
int main()
{
    scanf("%d%d",&n,&m);
    fac[0] = 1;
    Rep(i,m)fac[i] = 1ll * fac[i - 1] * i % mod;
    int all = (power(2,n,mod) - 1 + mod) % mod;
    int p = (power(2,n,mod) + mod - 1) % mod;
    A[1] = p;A[2] = 1ll * A[1] * (all - 1 + mod) % mod;
    for(int i = 3;i <= m;++ i)
    {
        f[i] = (1ll * A[i - 1] - f[i - 1] - 1ll * f[i - 2] * (i - 1) % mod * (all - (i - 2) + mod) % mod + 2ll * mod) % mod;
        A[i] = 1ll * A[i - 1] * (all - i + 1 + mod) % mod;
    }
    printf("%d\n",1ll * f[m] * power(fac[m],mod - 2,mod) % mod);
    return 0;
}

2.4690: Never Wait for Weights
题意:
有个天平它能反映出相对的重量,所以每次都会给你一些信息。
1.a比b轻w。2.询问a比b轻多少。
题解:带权并查集裸题,直接A上去然后推一波关系就行了。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)

using namespace std;
const int N = 100005;
int n,fa[N],val[N],m;
char s[5];
int find(int x)
{
    int t = fa[x];
    if(x != t)fa[x] = find(t);
    val[x] = val[t] + val[x];
    return fa[x];
}
int Merge(int x,int y,int z)
{
    int tx = find(x),ty = find(y);
    fa[ty] = tx;
    val[ty] = val[x] + z - val[y];
}
int main()
{
    while(~scanf("%d%d",&n,&m),n && m)
    {
        Rep(i,n)fa[i] = i,val[i] = 0;
        Rep(i,m)
        {
            int x,y,z;
            scanf("%s%d%d",s,&x,&y);
            if(s[0] == '!')
            {
                scanf("%d",&z);
                if(find(x) != find(y))
                    Merge(x,y,z);
            }
            else
            {
                if(find(x) != find(y))
                    puts("UNKNOWN");
                else
                {
                    int tx = find(x),ty = find(y);
                    printf("%d\n",val[y] - val[x]);
                }
            }
        }
    }
    return 0;
}

2017/1/18

1.[beijing2016]水晶
题解:
注意到那个三角的肯定有一个是中心点,只不过它没说。
那么对于一个中心点,正着的Y和倒着的Y只能选一组,这是肯定的。
那我们建图就是:
分类讨论,看它是属于正着的Y还是倒着的Y。
然后如果是正着的Y那就跟S连一下val.
倒着的Y就跟T连一下val.
中心点的话就把那6个位置都连一下,然后都连inf。
注意中心点也可以被破坏掉,那么我们再把中心点拆点。
然后就做完了,但是注意,取模的时候,有负数的可能。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define v edge[i].to
#define fl edge[i].f
#define vfl edge[i ^ 1].f
#define mp make_pair
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
using namespace std;
map<pair<int,int>,int >val;
map<pair<int,int>,int >gid;
const int M = 4000005;
const int N = 200005;
int S,T,head[N],cnt,dep[N],q[N],n,va[N],cor[N][2];
const int inf = 1e9;
struct Edge{int next,to,f;}edge[M];
void save(int a,int b,int c)
{
    edge[cnt] = (Edge){head[a],b,c},head[a] = cnt ++;
    edge[cnt] = (Edge){head[b],a,0},head[b] = cnt ++;
}
bool bfs()
{
    Rep(i,T)dep[i] = -1;dep[0] = 0;
    int h = 0,t = 0;q[0] = S;
    while(h <= t)
    {
        int x = q[h ++];
        RepG(i,x)if(dep[v] == -1 && fl)dep[q[++ t] = v] = dep[x] + 1;
    }
    return dep[T] > 0;
}
int dfs(int x,int f)
{
    if(x == T)return f;
    int s = 0,ff;
    RepG(i,x)if(dep[v] == dep[x] + 1 && f && fl && (ff = dfs(v,min(f,fl))))
        s += ff,fl -= ff,vfl += ff,f -= ff;
    if(!s)dep[x] = -1;
    return s;
}
int mxf(){int s = 0;for(;bfs();s += dfs(S,inf));return s;}
int tot;
int main()
{
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    Rep(i,n)
    {
        int x,y,z,c;
        scanf("%d%d%d%d",&x,&y,&z,&c);
        x -= z,y -= z; // [3000,3000];
        if((x + y) % 3 == 0)
        {
            c *= 11;
            val[make_pair(x,y)] += c;tot += c;
        }
        else 
        {
            c *= 10;
            val[make_pair(x,y)] += c;tot += c;
        }
    }
    int zz = 0;
    for(map<pair<int,int>,int > :: iterator it = val.begin();it != val.end();++ it)
    {
        pair<int,int> idx = it -> first;
        int c = it -> second;
        ++ zz;gid[idx] = zz,va[zz] = c,cor[zz][0] = idx.first,cor[zz][1] = idx.second;
    }
    T = 2 * zz + 1;
    Rep(i,zz)
    {
        int x = cor[i][0],y = cor[i][1];
        int _x,_y,z;
        if(((x + y) % 3 + 3) % 3 == 0)
        {
            save(i,i + zz,va[i]);

            _x = x - 1,_y = y - 1;
            z = gid[mp(_x,_y)];
            if(z)save(z,i,inf);

            _x = x,_y = y + 1;
            z = gid[mp(_x,_y)];
            if(z)save(z,i,inf);

            _x = x + 1,_y = y;
            z = gid[mp(_x,_y)];
            if(z)save(z,i,inf);

            _x = x + 1,_y = y + 1;
            z = gid[mp(_x,_y)];
            if(z)save(i + zz,z,inf);

            _x = x - 1,_y = y;
            z = gid[mp(_x,_y)];
            if(z)save(i + zz,z,inf);

            _x = x,_y = y - 1;
            z = gid[mp(_x,_y)];
            if(z)save(i + zz,z,inf);
        }
        else if(((x + y) % 3 + 3) % 3 == 1) // S侧 
            save(S,i,va[i]);
        else save(i,T,va[i]);// T侧 
    }
    printf("%.1f\n",(tot - mxf()) / 10.0);
    return 0;
}

感觉自己的……收flag的水平越来越高了。
北京冬令营结束后,那天zxn没对拍居然A掉了一个斜率优化……
虽然赛后贾老师似乎还是觉得我那场考试失误了……
的确……放着100+的暴力不写……
但是确实没想到嘛……
最后混了个Rank10……
总结结束后,在机房里颓了一天
然后现在开始写FFT的裸题。
结果还被卡精度了……

2017/1/20
1.两个串:
给你两个串。
问你这两个串的匹配位置,带通配符。
拆成平方形式,然后发现可以卷积,FFT。
注意,FFT算的时候数字越小越好,还是把那个ASCII码减掉比较好,不然会被出题人卡精度。呵呵。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define R0(i,n) for(int i = 0;i < n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
using namespace std;
const int N = 400005;
struct Virt
{
    double r,i;
    Virt(double R = 0.0,double I = 0.0){this -> r = R,this -> i = I;}
    Virt operator + (const Virt &x){return (Virt){x.r + r,x.i + i};}
    Virt operator - (const Virt &x){return (Virt){r - x.r,i - x.i};}
    Virt operator * (const Virt &x){return (Virt){r * x.r - i * x.i,x.r * i + r * x.i};}
};
const double PI = acos(-1);
int n,m,LEN,Rev[N],ans1[N],ans2[N],ans3[N];
void Rader()
{
    int LG = 0;
    for(LEN = 1;LEN <= 2 * n;LEN <<= 1,LG ++);
    R0(i,LEN)Rev[i] = Rev[i >> 1] >> 1 | ((i & 1) << (LG - 1));
}
void FFT(Virt *F,int ty = 1)
{
    R0(i,LEN)if(i < Rev[i])swap(F[i],F[Rev[i]]);
    for(int i = 1;i < LEN;i <<= 1)
    {
        Virt wn(cos(PI / i),sin(PI / i) * ty);
        for(int j = 0;j < LEN;j += (i << 1))
        {
            Virt w(1,0);
            for(int k = 0;k < i;++ k,w = w * wn)
            {
                Virt u = F[j + k],v = w * F[j + k + i];
                F[j + k] = u + v,F[j + k + i] = u - v;
            }
        }
    }
    if(ty == -1)R0(i,LEN)F[i].r /= LEN;
}
Virt A[N],B[N],C[N],C1[N],C2[N],C3[N];
char s[N],s2[N];
int a[N],b[N],a2[N],b2[N];
bool f[N];
int main()
{
    scanf("%s",s);n = strlen(s);
    R0(i,n)a[i] = s[i] - 'a' + 1,a2[i] = a[i] * a[i];
    scanf("%s",s2);m = strlen(s2);
    R0(i,m){b[i] = (s2[m - i - 1] == '?' ? 0 : (s2[m - i - 1] - 'a' + 1));b2[i] = b[i] * b[i];}

    Rader();
    R0(i,LEN)A[i].r = A[i].i = B[i].r = B[i].i = 0;
    R0(i,n)A[i].r = a2[i],A[i].i = 0;
    R0(i,m)B[i].r = b[i],B[i].i = 0;
    FFT(A),FFT(B);
    R0(i,LEN)C1[i] = A[i] * B[i];
    FFT(C1,-1);

    R0(i,LEN)A[i].r = A[i].i = B[i].r = B[i].i = 0;
    R0(i,n)A[i].r = a[i],A[i].i = 0;
    R0(i,m)B[i].r = b2[i],B[i].i = 0;
    FFT(A),FFT(B);
    R0(i,LEN)C2[i] = A[i] * B[i];
    FFT(C2,-1);

    R0(i,LEN)A[i].r = A[i].i = B[i].r = B[i].i = 0;
    R0(i,n)A[i].r = 1,A[i].i = 0;
    R0(i,m)B[i].r = b[i] * b2[i],B[i].i = 0;
    FFT(A),FFT(B);
    R0(i,LEN)C3[i] = A[i] * B[i],C2[i].r *= 2.0;
    FFT(C3,-1);

    R0(i,n)ans1[i] = (int)(C1[i].r + 0.5);
    R0(i,n)ans2[i] = (int)(C2[i].r + 0.5);
    R0(i,n)ans3[i] = (int)(C3[i].r + 0.5);

    //R0(i,n)C[i] = C1[i] - C2[i] + C3[i];
    int cur = 0;

    for(int i = m - 1;i < n;++ i)if(ans1[i] - ans2[i] + ans3[i] == 0)cur ++;
    printf("%d\n",cur);
    for(int i = m - 1;i < n;++ i)if(ans1[i] - ans2[i] + ans3[i] == 0)printf("%d\n",i - m + 1);
    return 0;
}

2017/1/21

1.4078: [Wf2014]Metal Processing Plant
把边建出来。
枚举d(A),用2-SAT+双指针来扫d(B).
复杂度是n4,会TLE.
发现答案的变化只有O(n)次。
那就随便枚举一下d(A),然后二分d(B),然后再二分d(A),总次数是n次,每次是n2logn,就可以过了。
但是……我不会写啊QAQ
2.3622: 已经没有什么好害怕的了
肯定dp。
f[i][j]表示到了第i个,前面比a[i]小的还至少j个没被匹配的方案数。
f[i][j]=f[i1][j]+f[i1][j1](lenj+1);
前一项代表不和小的匹配,后一项代表和小的匹配。
那个乘法是肯定的,因为我可以选的一共就那么多嘛。
然后最后能算出来f[n][m],其中m=(n+k)/2.
能算出这个之后,就可以容斥了对吧。
Q:为什么那个转移是至少
A:想一想就知道辣……
然后容斥。
现在来冷静下,这个数组似乎算的方案数还不太对。
因为我们发现f[n][i]的值似乎少了一个阶乘,因为它们可以任意排列?fac[ni]
那么记录一个数组g,g[i]=f[n][i]fac[ni];
然后容斥,用至少i个来推恰好k个,怎么推呢?
ans=i>=k(ik)(1)ikg[i]
就做完了对吧。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
using namespace std;
const int N = 2005;
const int mod = 1e9 + 9;
int fac[N],n,k,f[N][N],a[N],b[N],g[N],h[N],rev[N];
void Add(int &x,int y){(x += y) %= mod;}
int pw(int x,int y){int re = 1;while(y){if(y & 1)re = 1ll * x * re % mod;y >>= 1;x = 1ll * x * x % mod;}return re;}
int calc(int j,int i,int val)
{
    int re = 0,z = i - j;
    re = 1ll * val * fac[i] % mod * rev[i - j] % mod * rev[j] % mod;
    if(z & 1)return (mod - re) % mod;
    else return re;
}
int main()
{
    scanf("%d%d",&n,&k);
    Rep(i,n)scanf("%d",&a[i]);
    Rep(i,n)scanf("%d",&b[i]);
    sort(a + 1,a + 1 + n);
    sort(b + 1,b + 1 + n);
    f[0][0] = 1;    
    int m = 0;

    for (int i = 1; i <= n; ++i) 
    {
        while (m < n && b[m + 1] < a[i]) ++ m;

        for (int j = 0; j <= m; ++j) 
        {
            Add(f[i][j],f[i - 1][j]);
            if (j > 0) Add(f[i][j],1ll * f[i - 1][j - 1] * (m - j + 1) % mod);
        }
    }
    fac[0] = 1,rev[0] = 1;
    for(int i = 1;i <= n;++ i)fac[i] = 1ll * fac[i - 1] * i % mod,rev[i] = pw(fac[i],mod - 2);

    for(int i = 0;i <= n;++ i)g[i] = 1ll * f[n][i] * fac[n - i] % mod;//,printf("now %d %d\n",i,g[i]);
    if((n - k) % 2)return puts("0"),0;
    k = (n - k) / 2; // k个 
    k = n - k;
    int ans = 0;
    for(int i = k;i <= n;++ i)Add(ans,calc(k,i,g[i]));
    printf("%d\n",ans);
    return 0;
}

一开始把k算错了QAQ。

3.4245: [ONTAK2015]OR-XOR
其实这个题以前做过……
先来回忆一下思路吧。
连续,先想前缀和一类东西。
求前缀XOR.
弄出来之后,逐位确定。
怎么逐位确定?如果一段有超过m个0,且最后那个n为0,那么就可以选,把1的位del掉。
就能做完了。

#include<bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
using namespace std;
int m,n;
long long a[500001],ans;
bool del[500001];
int main ()
{
    scanf("%d%d",&n,&m);
    Rep(i,n)scanf("%lld",&a[i]),a[i] ^= a[i - 1];
    for(int bit = 62;~ bit; -- bit) 
    {
        int cur = 0;
        Rep(i,n)
            if(!del[i] && !((1ll << bit) & a[i]))cur ++;
        if(cur < m || (a[n] & (1ll << bit)))ans |= 1ll << bit;
        else Rep(i,n)if(!del[i] && ((1ll << bit) & a[i]))del[i] = 1;
    }
    printf("%lld\n",ans);
    return 0;
}


2017/1/22
1.1095: [ZJOI2007]Hide 捉迷藏
动态点分治+rmqlca。

#include <bits/stdc++.h>
#define v edge[i].to
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
using namespace std;
const int N = 100005;

int node_cnt,rt,tb[N],sz[N],head[N],cnt,black,c[N],tim,tid[N],rmq[N * 2][18],lg[N * 2],fa[N],dep[N],n,m;
bool done[N];
struct Edge{int next,to;}edge[N << 1];
void save(int a,int b){edge[cnt] = (Edge){head[a],b},head[a] = cnt ++;}
struct Heap
{
    priority_queue<int>h,d;
    void ins(int val){ h.push(val); }
    void del(int val){ d.push(val); }
    void fi() { while(!d.empty() && h.top() == d.top()) h.pop(), d.pop(); }
    void pop(){ fi(); h.pop(); }
    int fir(){ fi(); return h.top();}
    int sec(){ fi(); int x,y; x = fir(); pop(); y = fir(); ins(x);return y;}
    int total(){ return h.size() - d.size();}
    void change(int x,int ty) { if(ty)ins(x); else del(x); }
}h1[N],h2[N],ans;
void add_ans(Heap &s){if(s.total() >= 2)ans.ins(s.fir() + s.sec());}
void del_ans(Heap &s){if(s.total() >= 2)ans.del(s.fir() + s.sec());}
void get(int x,int f)
{
    sz[x] = 1;tb[x] = 0;
    RepG(i,x)
    {
        if(done[v] || v == f)continue;
        get(v,x);
        sz[x] += sz[v];
        tb[x] = max(tb[x],sz[v]);
    }
    tb[x] = max(node_cnt - sz[x],tb[x]);
    if(tb[x] < tb[rt])rt = x;
}
void dfs(int x,int f,int dep,Heap &s){s.ins(dep);RepG(i,x)if(!done[v] && v != f)dfs(v,x,dep + 1,s);}
void solve(int x,int s,int org = 0)
{
    tb[0] = 1000000;node_cnt = s;
    //dfs(x,0,1,h1[x]);
    rt = 0;get(x,0);
    dfs(x,0,1,h1[rt]);
    x = rt;done[x] = 1;
    h2[x].ins(0);
    //printf("PUSH %d %d\n",x,0);
    //dfs(x,0,1,h1[x]);
    if(org)
    {
        fa[x] = org;
        h2[org].ins(h1[x].fir());
    //  printf("PUSH %d %d\n",org,h1[x].fir());
    }
    get(x,0);
    RepG(i,x)if(!done[v])solve(v,sz[v],x);
    add_ans(h2[x]);
}
void rmq_dfs(int x,int fa)
{
    rmq[++ tim][0] = dep[x] = (dep[fa] + 1);tid[x] = tim;  // tid表示x的第一次出现位置 
    RepG(i,x)if(v != fa)rmq_dfs(v,x),rmq[++ tim][0] = dep[x];
}
void rmq_build(int s)
{
    for(int i = 2;i <= s;++ i)lg[i] = lg[i >> 1] + 1;
    for(int j = 1;j <= lg[s];++ j)
        for(int i = 1;i <= (s + 1 - (1 << j) );++ i)
            rmq[i][j] = min(rmq[i][j - 1],rmq[i + (1 << (j - 1))][j - 1]);
}
int rmq_dep(int a,int b) // return dep[lca(a,b)];
{
    a = tid[a],b = tid[b];
    if(a > b)swap(a,b);
    int s = lg[b - a + 1];
    return min(rmq[a][s],rmq[b - (1 << s) + 1][s]);
}
int dis(int a,int b){return dep[a] + dep[b] - 2 * rmq_dep(a,b);}
void Modify(int x,int col)
{
    del_ans(h2[x]); h2[x].change(0,col); add_ans(h2[x]);
    int y = x;
    while(fa[x])
    {
        del_ans(h2[fa[x]]);
        if(h1[x].total())h2[fa[x]].del(h1[x].fir());
        h1[x].change(dis(fa[x],y),col);
        if(h1[x].total())h2[fa[x]].ins(h1[x].fir());
        add_ans(h2[fa[x]]);
        x = fa[x];
    }
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("1095.out","w",stdout);
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    Rep(i,n - 1){int a,b;scanf("%d%d",&a,&b);save(a,b),save(b,a);}
    solve(1,n);rmq_dfs(1,0);rmq_build(tim);
    //Rep(i,n)printf("%d %d\n",i,fa[i]);
    scanf("%d",&m);
    black = n;
    while(m --)
    {
        char op[5];
        scanf("%s",op);
        if(op[0] == 'C')
        {
            int x;scanf("%d",&x);
            Modify(x,c[x]);
            c[x] ? ++ black : -- black;
            c[x] ^= 1;
        }
        else
        {
            if(black < 2)printf("%d\n",black - 1);
            else printf("%d\n",ans.fir());
        }
    }
    return 0;
}

具体想法就是建出点分树然后搞一搞。

2.Codeforces Round #257 (Div. 1)D. Jzzhu and Numbers
orz fsf系列。


子集和变换:
定义莫比乌斯变换为:f(S)=TSf(T)
把这个东西看作是关于S的函数,这样知道了f能弄出来S的每个函数值,但是代价太大了,复杂度应该是3n
考虑前缀和思想。
我们定义fi(S)表示强制以Si+1...n这段后缀结尾,前面1...i随便确定(但是需要是S的子集)的f的和。
更进一步地说,要确定的f(S)这个总函数其实就是TS,即确定了1...n,以结尾的后缀的f的和。
那么注意到,如果第i位是0的话,应该和前一个一样。
如果是1的话,那么这个点的fi(S)=fi1(S)+fi1(Si).
这就是子集和变换。
没什么用,但是如果想计算两个集合幂级数的卷积,就可以先计算出子集和变换,再把它逆变换回去。
逆变换可以容斥,直接回去就好了。


要求的是子序列与起来为0的方案。
令f(S)表示当前至少弄到了S的方案数。
要求的是:f(S)=1<=i<=ngi(S).
然后就要代入点值。f(S)=
超集和变换。


2017/1/25
前几天已经跑步去了……
给小朋友们讲了讲莫比乌斯反演,感觉自己需要学习一个。
1.2301: [HAOI2011]Problem b
可以用nlogn的反演过此题(滑稽)
2.2820: YY的GCD
换元法就行了。
3.4407: 于神之怒加强版
枚举d,然后换元,之后得到一个可以素数筛的东西。
4.2154: Crash的数字表格
枚举d随便算一算。
5.2693: jzptab
同上一个题。
6.wyx:
题解:http://blog.csdn.net/werkeytom_ftd/article/details/50485139
7.3529: [Sdoi2014]数表
离线把a排序,然后树状数组维护那个前缀和,之后分块暴力。
8.3994: [SDOI2015]约数个数和
我记得没啥难度。
常用的推导:

d|(i,j)<=>d|i,d|j

ij[(i,j)=1]=d=1NNdMdμ(d)

nimjij=(n+1)n(m+1)m/4
[xsfn]<=>[μ(x)2=1]
看到gcd,规避问题,把它写成函数然后反演。


WC铁牌滚粗。
开始制定详细的刷题计划= =
然而回来之后就生病了……
第一天重新学习了一下SAM,但是总感觉卡在一个很奇怪的地方。
今天算是第二天吧。
敲了一个后缀数组板子题,打了一个CF的VP。
网址:http://codeforces.com/contest/569/
A题直接暴力就行了,看起来就很靠谱。
B题的话,题意是让你找出N+1个点的集合划分 - N个点的集合划分。
这里要用一个东西叫做Bell数。
就是N个集合的划分数。
Bi+1=jBj(ij)
考虑证明,假设这个东西和某几个东西一起丢进去,这样就行了。
然后就没了。
C题是个2SAT,还没有写。DE没有看。
还有一个题目是:
Bzoj 3601 一个人的数论。

i=1n[(i,n)=1]ie

i=1nd|n,d|iμ(d)ie

d|nμ(d)dei=1ndie

f(n)=μ(n)ne

g(n)=i=1nie

d|nf(d)g(nd)

唯一能解决的方法是,猜这个g是关于ne+1次多项式。
那么:
g(n)=i=0e+1aini

我能怎么办,我也很绝望啊
带入:
d|nf(d)i=0e+1ai(nd)i

i=0e+1aid|nf(d)(nd)i

然后……
hi(n)=d|nf(d)(nd)i
绝望到底
注意它们两个都是积性函数,那么狄利克雷卷积也肯定是辣!
最终答案:
i=0e+1aihi(n)

那么就是说,如果能暴力出h就行了。
n实在很大,那么只能考虑一个pk.
hi(pk)=j=0kμ(pj)pjepi(kj)

mu(pk)?
hi(pk)=(pk)i(pk1)ipe=pki(1pei).

好像TMD快速幂就行了……?
那这样总体来讲就是高斯消元求个多项式系数,然后推积性函数式子,然后答案就是e+1i=0aihi(n)
指数取模的时候,要对mod-2取模……

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define Dwn(i,n) for(int i = n;i ;i --)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
using namespace std;
const int N = 115;
const int M = 1105;
int e,w,b[N],a[N][N],X[N],p[M],q[M];
const int mod = (int)1e9 + 7;
int pw(int x,long long y,int m)
{
    int re = 1;
    if(y < 0)return pw(pw(x,m - 2,m),-y,m);
    while(y)
    {
        if(y & 1)re = 1ll * x * re % mod;
        y >>= 1; x = 1ll * x * x % mod;
    }
    return re;
}
void print()
{
    rep(i,0,e)
    {
        rep(j,0,e)cout << a[i][j] << ' ';
        cout << b[i];
        cout << endl;
    }
}
void Gauss()
{
    int j,k;
    ++ e;
    rep(i,0,e)
    {
        for(j = i;j <= e;++ j) if(a[j][i]){j = i;break;}
        if(i != j){for(k = 0;k <= e;++ k)swap(a[i][k],a[j][k]);swap(b[i],b[j]);}
        int r = pw(a[i][i],mod - 2,mod);
        for(j = i + 1;j <= e;++ j)
        {
            if(!a[j][i])continue;
            int z = 1ll * a[j][i] * r % mod;
            for(k = i;k <= e;++ k)a[j][k] = (a[j][k] - 1ll * a[i][k] * z % mod + mod) % mod;
            b[j] = (b[j] - 1ll * b[i] * z % mod + mod) % mod;
        }
    }
    dwn(i,e,0)
    {
        X[i] = b[i];
        for(j = i + 1;j <= e;++ j)X[i] = (X[i] - 1ll * X[j] * a[i][j] % mod + mod) % mod;
        X[i] = 1ll * X[i] * pw(a[i][i],mod - 2,mod) % mod;
    }
    -- e;
    //rep(i,0,e + 1)printf("!!%d ",X[i]);
}
void init()
{
    for(int i = 0;i <= e + 1;++ i)
    {
        int ie = pw(i + 1,e,mod);
        for(int j = 0;j <= e + 1;++ j)a[i][j] = pw(i + 1,j,mod);
        if(i)b[i] = (b[i - 1] + ie) % mod;
        else b[i] = ie;
    }
}
int solve(int P,int Q,int i){return 1ll * pw(P,1ll * Q * i,mod) * ( (1ll - pw(P,e - i,mod) + mod) % mod ) % mod;}
int main()
{
    scanf("%d%d",&e,&w);
    init();
    Gauss();
    rep(i,1,w)scanf("%d%d",&p[i],&q[i]);
    int ans = 0;
    rep(i,0,e + 1)
    {
        int re = X[i];
        rep(j,1,w)re = (1ll * re * solve(p[j],q[j],i)) % mod;
        ans = (ans + re) % mod;
    }
    printf("%d\n",ans);
    return 0;
}

CodeforcesRound213Div1翻译和题解和程序暂未上传T.T
2/14:但是已经算是完成了ABD,晚上回去把CDE写完。
2/14:情人节,打开http://codeforces.com/contest/446,去参节FFF团!
2/14有一场Codeforces,参加并掉rating!
事情就是这样= =


2017/2/15

fsf出的模拟赛= =。
先说下今天做(嘴巴)的CF。
场次是215.
A题发现读错题了= =。不过也是前缀和瞎扫一扫就完了。
B题的话xjb用map搞搞……

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define rep(i,a,b) for(int i = a;i <= b;++ i)
#define dwn(i,a,b) for(int i = a;i >= b;-- i)
#define Dwn(i,n) for(int i = n;i;-- i)
using namespace std;
const int N = 200005;
map<int,int>a,b;
vector<int>vec;
int val[N],n,m,p,q[N];
void solve(int x)
{
    int h = 0,t = 0;
    b.clear();
    for(int i = x;i <= n;i += p)
    {
        q[++ t] = val[i];b[val[i]] ++;
        if(t - h == m)
        {
            if(a == b)vec.push_back(h * p + x);
            int y = q[++ h];
            b[y] --;if(!b[y])b.erase(y);
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    Rep(i,n)scanf("%d",&val[i]);
    Rep(i,m){int x;scanf("%d",&x);a[x] ++;}
    Rep(i,p)solve(i);
    sort(vec.begin(),vec.end());
    printf("%d\n",vec.size());
    for(int i = 0;i < vec.size();++ i)printf("%d ",vec[i]);
    return 0;
}

CD好像已经快忘记了= =
我们来说E。
E
题意:
求n个元素的不互相包含的区间集合个数,其中区间的[l,r]都小于等于m。
题解:
注意到n*m<=10W,显然n是小于m的,考虑n2m的做法。
f[i][l][r]表示到了第i个数值,已经打开了l个左括号,打开了r个右括号的合法的方案数。
定义合法:不存在任何一组区间包含。
那么注意到第i个值要么开一个左括号,要么开一个右括号,要么都开,要么都不开。
都不开很好办,具象化理解一下发现开一个左括号和开一个右括号的转移应该是差不多的。
发现如果现在打开了l个左括号,r个右括号的话, 那么显然有rl,考虑合法的方案,其实就只有一种方案,就是新加入的那个右括号和最早的左括号进行匹配,左括号直接放上去。


虽然不知道自己最近开了几场CF,不过也是开了一些QAQ
上午写了FF的D,精度爆了一年,学习了逆矩阵的写法。
下午看了一会番,然后开了一个A和B。回家看了看C。
翻译正在添加= =。

阅读更多
换一批

没有更多推荐了,返回首页