我要做一名专业的TC/CF翻译、、、

(感觉自己需要勤勉地翻译题目,这样翻成中文题目之后自己就比较喜欢看,然后就能催促自己做题。)
不过暂时还没有翻译(逃
一边做一边补。


另外如红黑联盟/蓝汛什么乱七八糟的爬虫网站真的挺烦人的,如果在这类网站看到这篇文章的话……
记得要点原链接:blog.csdn.net/zxn0803
虽然说csdn的博客就是用来爬的……
然而也不能太过分了不是?



TopCoder

SRM670Div1

  • Level1

给个合法的括号序列s,求和s长度相同且有尽可能长的LCS的合法的括号序列个数。(n<=50)

  • Level2

给棵n个节点的树。A有一些红色节点,B有一些蓝色节点,A先动,他可以把自己的每个红色节点要么挪到相邻的点上,要么不动。B后动,B可以把蓝色节点挪到相邻的点上或者不动。当有个点使得蓝红两色重合了,那么B就赢了。问A尽可能拖时间的话,B采取最优策略会导致几回合后A打出GG.(n<=50)

嘴巴题解:

  • Level1:

暴力DP就没了吧?
f[i][j][len][left]+=f[i1][j][len1][left1](s[j]==left)+f[i][j1][len][left]+f[i1][j][len1][left+1](s[j]==right)
应该没了?
(有毒,感觉题解好像不是这样暴力?)

  • Level2:

先把蓝色点都丢到队列里bfs,然后对于红色节点记忆化一下能续的最久的节点,跑到那个节点去就行了。总之是个记忆化的题。


CodeForces

19E
- E:一个n个点m条边的无向图,问哪些边删掉可以让原图变成个二分图。

题解:
考虑dfs树,对于树边和非树边进行统计,路径求交(dfs+子树和)即可。

36E
n个点m条边的无向图,找出两条路径使得每条边仅被经过一次。

题解:连条边,构造欧拉路径,好像有点难写。

37E
n*m的白色矩阵,每次可以选个联通块(四联通的那种)然后把它变成黑色或者白色,问最少染几次能变成原来的矩阵。

题解:可以构造,实际上从一个点出发的话相同颜色就连0,否则连1,每个点跑个spfa就行。
但是似乎和这个点的目标状态有关,所以还是要注意一下的。

209C
n个点m条边的无向图,问添加多少边能让该图存在过点1的欧拉回路。

题解:其实挺好写的,贪心连一连边就行了,但是有自环……所以判断的时候要注意一下,不能用并查集判,而是要看度数。

Codeforces按场次:

Codeforces Round #174 (Div. 1)
A
题意:有三种操作:
1.[1,ai]区间+k.
2.push_back(val).
3.末尾弹出一个数。
题解:虽然说自己当时SB写的树状数组……
其实就是把标记向前转移一下就行了,复杂度是O(nlogn).
B
题意:
题意是给你个数列a2..an,把a1从1开始循环到n1.
每次循环的时候,如果是奇数次循环,那么x+=a[x],y+=a[y].否则x=a[x],y+=a[y].
容易发现,它有可能数组越界,也有可能卡死在里面。
如果越界就直接循环结束输出y
如果卡死在里面输出-1.
题解:
维护一个跳转的数组trans[x][2],然后看那些点是不是在环上就行了。
C
题意:给你n种硬币,第i种硬币价值ai,然后给m个限制cnt[xi]小于cnt[yi],即第xi种硬币的数目要小于yi的数目。
保证x集合的数两两不同,y集合中的也是。
求正好凑到T的方案数。
题解:暴力DP+dfs.
D
题意:给个序列an,让你改一改这个序列,满足对于任意i都能有ai=ai+1个连续的自然数相加,求最少需要改多少个数字。
题解:
分奇数偶数讨论,容易发现奇数的情况肯定是aj|ai的。
偶数的情况算了算应该是aimodaj==1/2kai这样类似的东西。
然后就可以dp了,用f[i]表示强制到i不变的最小改变次数。当然也有其他写法。
E:
题意:给n个不同权值,每个权值代表第i个人的能力,保证能力大的会战胜能力小的。有m次操作,每次翻转权值[a,b]的胜负性,即[a,b]中,权值小的会战胜权值大的。一个区间可能被翻转多次。问三元环(a能战胜b,b能战胜c,c能战胜a)的个数。
题解:
这个题如果SB如我的话应该会想到二维线段树。
弄个白色边弄个黑色边,会发现合法的三元环肯定是异色的三元环。
但是我们再仔细一看发现并不能直接求异色三元环的个数,这样会崩,因为有那种长得很奇怪的能同时战胜两个对手的三元环,他们也是异色的。
可以算反面,用所有的三元环减掉存在能赢两局的点的三元环的数目就行了。
sort之后,考虑第i个节点,它前面的有x个能战胜i,后面有y个能战胜i.
那这个节点能产生的不合法的三元环的个数显然是(x+y)(x+y1)/2.
注意到这个可以不用二维线段树,它是可以扫描线的,那么就做完了。

Codeforces Round #FF (Div. 1)

A. DZY Loves Sequences
给一个序列,在有一次可更改一个位置的值的机会下,这个序列的严格上升序列。

B. DZY Loves Modification
给一个n*m矩阵,每次对一行或者一列进行操作,获得当前的这行(列)的所有元素和的价值,然后把这行(列)每个元素都减掉p。求强制k次操作后的最大价值。
C. DZY Loves Fibonacci Numbers
区间斐波那契加,区间求和。
线段树。希望自己能写的出来(应该还是不成问题的吧……)
D.DZY Loves Games
给个N个点M条边的无向图,DZY在1号点,有一些点有炸弹,DZY会在这个图里随机游走[从一个点出去每条边的走的概率是相等的],每到一个炸弹就会炸掉他一条命。n点必定有炸弹,1号点必定没有炸弹。求走到n点被炸一次恰好剩一条命的概率。注意,一共最多只有100个炸弹。
E.不好意思没看QAQ


A.枚举中间断点,然后dp。
B.如果只做列或者只做行,那么答案是不会受影响的。
考虑如果在行里做了i次,那么在列中就做了k-i次。考虑先选行再选列,会发现列j的价值是:sum[j] - i * p.
因为要做k-i次,那么最终答案带来的代价就是i(ki)p
那么枚举i,行列分开贪心即可。
C.考察线段树的基本知识。
D.考虑构造一个矩阵w,满足w[u][v]表示第u个陷阱走到第v个陷阱的路径中不经过任何陷阱的概率。那么我们只需要把这个矩阵w乘上k-2次,然后乘一下1到i的概率,加起来就行了。
现在考虑怎么去算这个w[u][v].
暴力的话一定是这样做的:
no(i)代表第i个点不是陷阱。
out[x]代表x有几条出边是u.
枚举u,转移方程是

f[x]=(vout and no(v)f[v])+out[x]d[x]

发现有环,那就只能高消。
转移矩阵G怎么建?
显然out[x]是个常数,就是邻接矩阵con[u][x]的值,那么等式右端就是out[x]/d[x],左端变成f[x]vf[v]/d[x].
那么转移矩阵肯定是左边的那个矩阵。解出f数组之后,用f数组去乘这个转移矩阵就会等于常数矩阵,那么Gf=B,f=G1B.求出G的逆矩阵然后直接算就行辣!


Codeforces Round #372 (Div. 1)

B. Complete The Graph
题意:给一个初始的图,有些边没有赋值,要求把这些边赋成正值之后S>T最短路恰好为L.输出方案。
题解:先用极大边把图联通,然后尝试地把边都改成1,直到改到L以下,这时候这条边可以影响L,更改这条边的权值使得它变成L显然是可以计算的,那么把这条边的权值改成这样的权值,其他都变成inf就行了。

#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)
#define v edge[i].to
#define mid (l + r >> 1)
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
using namespace std;
const int N = 10005;
typedef long long LL;
const LL inf = 1ll << 62;
struct Edge{int next,to,w;}edge[4 * N];
int head[N],cnt,vis[N],id[N];LL dis[N];
void save(int a,int b,int c){edge[cnt] = (Edge){head[a],b,c},head[a] = cnt ++;}
void change(int i,int c){edge[2 * i - 1].w = edge[2 * i - 2].w = c;}
int eq[N][3],q[N * 100],S,T,L,n,m;
LL spfa()
{
    int h = 0, t = 0;
    vis[q[0] = S] = true;
    Rep(i,n)dis[i] = inf;
    dis[S] = 0;
    while(h <= t)
    {
        int x = q[h ++];
        RepG(i,x)
        {
            if(dis[v] > dis[x] + edge[i].w)
            {
                dis[v] = dis[x] + edge[i].w;
                if(!vis[v])vis[q[++ t] = v] = 1;
            }
        }
        vis[x] = 0;
    }
    return dis[T];
}
int main()
{
    scanf("%d%d%d%d%d",&n,&m,&L,&S,&T);
    S ++,T ++;
    memset(head,-1,sizeof(head));
    Rep(i,m)scanf("%d%d%d",&eq[i][0],&eq[i][1],&eq[i][2]);
    Rep(i,m)
    {
        eq[i][0] ++,eq[i][1] ++;
        if(eq[i][2] != 0)
        {
            save(eq[i][0],eq[i][1],eq[i][2]);
            save(eq[i][1],eq[i][0],eq[i][2]);
        }
        else 
            id[i] = i;
            save(eq[i][0],eq[i][1],1e9);
            save(eq[i][1],eq[i][0],1e9);
            eq[i][2] = 1e9;
        }
    }
    if(spfa() < L)return puts("NO"),0;
    Rep(i,m)
    {
        if(id[i])
        {
            change(i,1);
            eq[i][2] = 1;
            LL d = spfa();
            if(d < L)
            {
                eq[i][2] = L - d + 1;
                change(i, eq[i][2]);
            }
        }
    }
    if(spfa() != L)return puts("NO"),0;
    puts("YES");
    Rep(i,m)
    {
        eq[i][0] --,eq[i][1] --;
        printf("%d %d %d\n",eq[i][0],eq[i][1],eq[i][2]);
    }
    return 0;
}

Codeforces382Div1

C:给一棵树,你可以染黑某些节点。求使得对于任意节点u,在距离k之内(含边界)存在至少一个黑色节点的染色方案数(n100,k20)。

题解:

f[i][j]表示以i为根的子树还能向”上”(距离<=j)贡献j的方案数。
f[i][j]表示以i为根的子树最远的白点已经到达了j的单位的方案数。
停,先来想想:
初值是啥?
f[i][k]=1,这个是肯定的。
f[i][1]=1?
这个也是肯定的,为什么呢?
就是说当前这个节点还没有和任何子树合并,导致现在它的确最远的白点已经到达了1.
和它的一个儿子更新之后就不一定是1了。
比如叶子节点a和它的父亲节点b,k=2.
更新之后肯定f[b][1]=1,f[b][2]=2,f[b][2]=1
意思是:a取黑色,b取白色,那么就是1.
a取黑色,b取黑色,那么就是2.
a取白色,b取白色,那么就是2.
a取白色,b取黑色,那么就是2.
所以更新完了所有子树可能根本就没有f[x][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)
#define v edge[i].to
#define RepG(i,x) for(int i = head[x];~ i;i = edge[i].next)
using namespace std;
const int N = 105;
int head[N],cnt,n,k,f[N][55],g[55];
struct Edge{int next,to;}edge[N<<2];
void save(int a,int b){edge[cnt] = (Edge){head[a],b},head[a] = cnt ++;}
const int mod = 1e9+7;
void Add(int &x,const int &y){x+=y;if(x>=mod)x-=mod;}
int mul (const int &x,const int &y){return 1ll * x * y % mod;}
int D(int t){return t + k;}
void dfs(int x,int fa = 0)
{
    f[x][D(-k)] = 1;
    f[x][D(1)] = 1;
    RepG(i,x)
    {
        if(v == fa)continue;
        dfs(v,x);
        memset(g,0,sizeof(g));
        for(int d = -k;d <= k;d ++)
        {
            if(!f[x][D(d)])continue;
            for(int d0 = -k;d0 <= k;++ d0)
            {
                if(!f[v][D(d0)])continue;
                int dd = d0 + 1,tmp,temp;
                if(d > 0 && d0 > 0)tmp = max(d,dd);
                else if(d > 0 && d0 <= 0)tmp = -d0 >= d ? dd : d;
                else if(d <= 0 && d0 > 0)tmp = -d >= d0 ? d : dd;
                else tmp = min(dd,d);
                Add(g[D(tmp)],mul(f[x][D(d)],f[v][D(d0)]));
            }
        }
        memcpy(f[x],g,sizeof(g));
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&k);
    Rep(i,n-1){int a,b;scanf("%d%d",&a,&b);save(a,b),save(b,a);}
    dfs(1);
    int ans = 0;
    for(int i = -k;i <= 0;++ i)Add(ans,f[1][D(i)]);
    printf("%d\n",ans);
    return 0;
}

Codeforces387:Div2
D:
题意:有两种轮胎A和B,A只能在气温>=0的时候用,B可以在任何天气用,但是B一共只能用k天,一开始你是处于使用A的状态,给出n天的气温,判断至少换多少次轮胎可以度过这n天。
我们肯定是可以DP的。
但是我们能想到有个东西叫做最大k子段和。
啊,那是什么?(去做bzoj)
于是我们想到,这道题啊,可以堆+贪心嘛。
然后就解决了QAQ

#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;
const int N = 2000005;
int n,m,a[N];
priority_queue<int>q;

int main()
{
    scanf("%d%d",&n,&m);
    int last = 0,t = 0,re = 0;
    bool flag = 1;
    Rep(i,n)
    {
        scanf("%d",&a[i]);
        if(a[i] < 0)
        {
            re ++,t += 2;
            if(last)q.push(-(i - last - 1));
            last = i;
            flag = 0;
        }
    }
    if(flag){puts("0"),exit(0);}
    if(m >= n){puts("1");exit(0);}
    if(a[n] < 0)t --;
    if(re > m)puts("-1"),exit(0);
    else 
    {
        while(!q.empty())
        {
            int y = q.top();q.pop();
            if(m >= re - y)
            {
                t -= 2;
                re -= y;
            }
            else break;
        }
        if(last != n && m >= re + (n - last))t --;
            return printf("%d\n",t),0;
    }
    return 0;
}

Codeforces388:Div2
E:
题意:给个初始排列P,随机选择一个区间打乱,求期望逆序对数目。
orz fsf系列。
其实是纸张题。

题解:首先把这段区间删除掉,然后变成期望的逆序对。
首先我们得算一下打乱一段区间的期望逆序对,这个可以直接递推,考虑把第i个数加入原来长度为(i-1)的序列,那么1/n的概率会有1个,1/n的概率有2个……
所以f[i]=f[i1]+i12
这样我们就知道长度为len的增加的期望逆序对数了。
然后考虑在打乱这个区间的时候,如果有一对原来的逆序对同时在这个区间里,那么这个逆序对就会消失。
那么一对逆序对消失的情况是:Al<=l<=r<=Ar,a[l]<=a[r].
总体会少多少呢?

lrl(nr+1)n(n+1)/2[ar<al]

扫描线+树状数组。

#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 = 100005;
int n,a[N],b[N];
void upd(int x){for(;x;x -= x & -x)b[x] ++;}
int qry(int x){int y = 0;for(;x <= n;x += x & -x)y += b[x];return y;}
long long t[N];
double ans,f[N];
void Upd(int x,long long y){for(;x;x -= x & -x)t[x] += y;}
long long Qry(int x){long long y = 0;for(;x <= n;x += x & -x)y += t[x];return y;}
int main()
{
    scanf("%d",&n);
    Rep(i,n)scanf("%d",&a[i]);
    Rep(i,n)ans += qry(a[i]),upd(a[i]);
    long long S = 1ll * (n + 1) * n / 2;

    Rep(i,n)
    {
        ans -= (double)(n - i + 1) * Qry(a[i]) / S;
        Upd(a[i],i);
    }
    Rep(i,n)f[i] = f[i - 1] + (double)(i - 1) / 2;
    Rep(i,n)ans += f[i] * (n - i + 1) / S;
    printf("%.9f\n",ans);
    return 0;
}

Codeforces Round #389 Div2:
%%%%mps
A和B都是水题,然后就看了看D和E就跑去玩耍了。
A:给个教室,求这个人坐在哪。
B:题意是有个人在打代码,然后打完了之后又想重写一遍,但是他键盘的某些位置被交换了,定义交换为把A键放到B键再把B键放到A键。
给两次打出的结果,问那些键盘的位置被交换了,这些位置被交换成什么。如果不可能,输出-1。
D:给n个串和它们的权值,你可以选择性地拼接一些串(不需要按照下标顺序,但每个串只许用一次),使得在最终拼出来的串是个回文串的同时,权值和最大。
E:有个人要给小朋友们切橘子,现在有n个橘子,每个橘子有ai瓣,这个人每次可以切开一个橘子(或者切开橘子的一部分),并把这个橘子(部分)分成两半。比如说:5切完会变成3和2.切出来的这个3和2还可以再切。但1不能再切。这个人切完橘子之后,就要把橘子分给小朋友,每个小朋友只能得到一次橘子,即如果这个人切了m份要分给k个小朋友,那么mk的部分都要扔掉。问得到最少瓣数橘子的小朋友最多能得到多少橘子。

B题解:
注意如果我们A已经变成了A的话,那么就不要再改了,因为再改就GG掉了。

D题解:
注意到原串和翻转的串要必须都出现一次,用map啥的塞进去查就行了。另外回文串特判。

E题解:
两种做法:
1.二分答案。
这是显然的二分答案,然后我们对于每个橘子直接切成大于等于当前答案的那么多瓣,如果用记忆化搜索实现的话,经过证明,每次切一个权值为W的橘子,复杂度是O(logW)的,这样变成O(nlog2W)的了,如果写的好是可以过的。然而我傻逼过不去。
2.开个W的桶,初始都加到桶里。
枚举权值,看是否可行,如果不可行的话,那么把这些橘子(个数为i)都切了,并且在W/2(W+1)/2这里放上这么多的橘子,但是要在(W+1)/2的位置打上答案减i的标记。
因为如果到了(W+1)/2的时候,我们的答案其实是不变的。


好久没有更新CF和TC了……
其实还是做了一部分题目的……


Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined)

题目毫无意义……?
A:输入一个数n,判断它的奇偶性。(原题并不是这样的QAQ)
B:按题意模拟QAQ
C:送分的hack题,都没什么意义QAQ
D:给你一个定义域为n的f函数,你可以自己设定一个定义域在n的函数g,g的值域是[1,m](m也可以自己设定),在设定一个h函数,它的定义域是[1,m],值域是[1,n],满足g[h[x]] = x,h[g[x]] = f[x]。输出g,h和m。
E:给你一棵树,每次可以合并两条长度相同的”链”,(链的定义是没有任何分支的路径),一直这样合并直到不能合并为止,求最后的点数。
F和G:没看题QAQ


题解:A,B,C略过,D题经过一番推导之后感觉离散值应该是m,然后这样就直接离散化构造就行了,如果构造不出来就打出GG。E题的话似乎dfs就可做了,因为最后不能合并的一定是根。然而我掉rating了……


阅读更多
换一批

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