牛客多校10 A Browser Games
思路很简单,在trie上建虚树,然后回溯染色处理。
//
// Created by Artist on 2021/8/26.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
const int maxn = 1e5+5;
char s[maxn][105];
int lst[maxn],stk[maxn<<1],tot,len[maxn],d[maxn<<1],p[maxn],fa[maxn<<1],ret[maxn<<1],sz[maxn<<1];
bool cmp(int x,int y){
for(int i=1;i<=min(len[x],len[y]);++i){
if(s[x][i]<s[y][i]) return 1;
if(s[x][i]>s[y][i]) return 0;
}
}
void addedge(int x,int y){
fa[y]=x;
}
int lca(int x,int y){
for(int i=1;i<=min(len[x],len[y]);++i){
if(s[x][i]!=s[y][i]) return i-1;
}
}
signed main(){
int n;scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",s[i]+1);
len[i]=strlen(s[i]+1);
p[i]=i;
}
// 先排序(建成trie)
sort(p+1,p+1+n,cmp);
// 再建虚树
tot=stk[*stk=1]=1;
for(int i=1;i<=n;++i){
int x=++tot; // 该字符串末尾对应的点(关键点)
d[x]=len[p[i]];
if(i==1) stk[++*stk]=x;
else {
int t=lca(p[i],p[i-1]); // lca的dfn
while(*stk>1 && t<=d[stk[*stk-1]]) addedge(stk[*stk-1],stk[*stk]),--*stk;
if(t!=d[stk[*stk]]) addedge(++tot,stk[*stk]),d[tot]=t,stk[*stk]=tot;
stk[++*stk]=x;
}
lst[p[i]]=x;
}
while(*stk>1) addedge(stk[*stk-1],stk[*stk]),--*stk;
// 处理答案
sz[1]=1; // 因为答案不能为0,所以令根节点与其他节点不一致
int ans=0;
for(int i=1;i<=n;++i){
for(int j=lst[i];j;j=fa[j]){
++sz[j];
}
}
for(int i=1;i<=n;++i){
for(int j=lst[i],k=0;j;k=j,j=fa[j]){
if(k && !sz[k]) ++ret[j],++ans;
if(!(--sz[j])) ans -= ret[j];
}
printf("%d\n",ans);
}
}
牛客多校7 J - xay loves floyd
for i from 1 to n
for j from 1 to n
for k from 1 to n
dis[i][j] <- min(dis[i][j], dis[i][k] + dis[k][j])
方法1
用dijkstra模拟这种floyd的跑法。
注意到,dis[i][j]
正确当且仅当以下两个条件之一满足:(1)dis[i][j]
本身正确(即,存在一条连接这两点的边,就是这两点的最短路)(2)dis[i][k]
和dis[k][j]
均正确,且k位于i到j的一个最短路上
我们先对每一个点跑一遍dijkstra得到正确的最短路数据。然后将图删剩仅满足条件(1)的边。
接着,再对每一个点跑一遍dijkstra,此时是在模拟错误的floyd,得到的结果是错误的floyd的结果。
我们记 g [ s ] [ t ] g[s][t] g[s][t] 表示 s s s 到 t t t 应用错误的 floyd 跑出的答案。
枚举中转点k,我们将(i,k)向(i,j)更新。注意必须有 j ≥ k j\geq k j≥k 更新才成立。
我们不以距离作为优先队列中用来排序的值。我们以中转点的序号作为用来排序的值,从小到大更新。
如果更新出的dis正确,将该路径当作一条边放入图中。
//
// Created by Artist on 2021/8/27.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
const int maxn = 2e3+4,maxm = 5e3+4;
const int inf = 0x3f3f3f3f;
vector<pii > G[maxn];
int g[maxn][maxn],dis[maxn][maxn];
priority_queue<pii,vector<pii >,greater<pii > > pq;
int n;
void dijkstra(int st){
for(int i=1;i<=n;++i) dis[st][i]=inf;
dis[st][st]=0;
while(!pq.empty()) pq.pop();
pq.push(mkp(0,st));
while(!pq.empty()){
pii cur=pq.top();pq.pop();
int u=cur.se,w=cur.fi;
if(dis[st][u]<w) continue;
for(auto [ww,v]:G[u]){
if(dis[st][v]>w+ww){
dis[st][v]=w+ww;
pq.push({dis[st][v],v});
}
}
}
G[st].clear();
// 清空并不会影响接下来的正确答案的dijkstra
for(int i=1;i<=n;++i){
if(dis[st][i]==g[st][i] && dis[st][i]<inf && st!=i)
G[st].pb(mkp(dis[st][i],i));
}
}
void solve(int st){
while(!pq.empty()) pq.pop();
for(auto i:G[st]) pq.push({0,i.se});
// first为中转点k
while(!pq.empty()){
pii cur=pq.top();pq.pop();
int u=cur.se,id=cur.fi;
for(auto [ww,v]:G[u]){
if(v<id) continue; // 只能往后更新
if(g[st][v]==dis[st][v]||dis[st][v]>=inf) continue;
if(dis[st][v]==dis[st][u]+ww){
pq.push({v,v});
G[st].push_back({dis[st][v],v});
g[st][v]=dis[st][v];
}
}
}
}
signed main() {
int m;scanf("%d%d",&n,&m);
memset(g,0x3f,sizeof(g));
for(int i=1,u,v,w;i<=m;++i){
scanf("%d%d%d",&u,&v,&w);
G[u].pb(mkp(w,v));
g[u][v]=w;
}
for(int i=1;i<=n;++i) g[i][i]=0;
for(int i=1;i<=n;++i){
dijkstra(i);
}
for(int i=1;i<=n;++i){
solve(i);
}
int ans=0;
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
if(g[i][j]==dis[i][j]||(g[i][j]>=inf&&dis[i][j]>=inf))
ans++;
printf("%d\n",ans);
}
方法2
这个依旧成立:dis[i][j]
正确当且仅当以下两个条件之一满足:(1)dis[i][j]
本身正确(即,存在一条连接这两点的边,就是这两点的最短路)(2)dis[i][k]
和dis[k][j]
均正确,且k位于i到j的一个最短路上
那么考虑设 g [ i ] [ j ] g[i][j] g[i][j] 为 i i i 到 j j j 的所有最短路上的点构成的集合。
f 1 [ i ] [ j ] f1[i][j] f1[i][j] 表示 d i s [ i ] [ j ] dis[i][j] dis[i][j] 是正确的, f 2 [ i ] [ j ] f2[i][j] f2[i][j] 表示 d i s [ j ] [ i ] dis[j][i] dis[j][i] 是正确的。
暴力用上述条件判断,是 O ( n 3 ) O(n^3) O(n3)的:
我们枚举每一个 i i i,再枚举 j j j,再枚举 k k k ,让 f 1 [ i ] [ k ] , f 2 [ k ] [ j ] f1[i][k],f2[k][j] f1[i][k],f2[k][j] 都为真,且 k ∈ g [ i ] [ j ] k\in g[i][j] k∈g[i][j]
那么我们考虑用bitset进行位压,优化转移。
我们把枚举 k k k 这一维度给优化掉。
因此变成:
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if((f1[i]&f2[j]&g[j]).any()){
f1[i].set(j);
f2[j].set(i);
}
}
}
//
// Created by Artist on 2021/8/27.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
const int maxn = 2e3+4,maxm = 5e3+4;
const int inf = 0x3f3f3f3f;
vector<pii > G[maxn];
int dis[maxn][maxn];
int tmp[maxn]; // 暂存序号
bitset<maxn> f1[maxn],f2[maxn],g[maxn];
int ID;
priority_queue<pii,vector<pii >,greater<pii > > pq;
int n;
void dijkstra(int s,int* d){
d[s]=0;
pq.push({0,s});
while(!pq.empty()){
pii cur=pq.top();pq.pop();
int u=cur.se,w=cur.fi;
if(d[u]<w) continue;
for(auto [v,ww]:G[u]){
if(dis[s][v]>w+ww){
dis[s][v]=w+ww;
pq.push({dis[s][v],v});
}
}
}
}
bool cmp(int x,int y){
return dis[ID][x] < dis[ID][y];
}
signed main() {
int m;scanf("%d%d",&n,&m);
memset(dis,0x3f,sizeof(dis));
for(int i=1,u,v,w;i<=m;++i){
scanf("%d%d%d",&u,&v,&w);
G[u].pb({v,w});
}
// 计算正确答案
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=n;++i){
dijkstra(i,dis[i]);
f1[i].set(i);
f2[i].set(i);
}
// 第一种情况
for(int u=1;u<=n;++u){
for(auto [v,w]:G[u]){
if(dis[u][v]==w){
f1[u].set(v);
f2[v].set(u);
}
}
}
for(int i=1;i<=n;++i){
// 先更新g
for(int j=1;j<=n;++j){
tmp[j]=j;
g[j].reset();
g[j].set(j);
}
ID=i;
sort(tmp+1,tmp+1+n,cmp);
for(int j=1;j<=n;++j){
int u=tmp[j];
for(auto [v,w]:G[u]){
if(dis[i][v]==dis[i][u]+w){
g[v] |= g[u];
}
}
}
// 再更新错误答案
for(int j=1;j<=n;++j){
if((f1[i]&f2[j]&g[j]).any()){
f1[i].set(j);
f2[j].set(i);
}
}
}
int ans=0;
for(int i=1;i<=n;++i) ans += f1[i].count();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
ans += dis[i][j]==inf;
printf("%d\n",ans);
}
P1050 [NOIP2005 普及组] 循环
假的扩展欧拉定理。。思路是题解1。
//
// Created by Artist on 2021/8/30.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
const int N = 1e3 + 3; // 数组大小(大数长度)
const int base = 1e8; // 压位,数组中一个位置储存一个1e8的数
int aux[N << 3];
struct bigint {
int s[N], l; // s[1]是最低位(是一个小于1e8的数)
void CL() {
l = 0;
memset(s, 0, sizeof(s));
}
void pr() {
printf("%d", s[l]);
for (int i = l - 1; i; i--)
printf("%08d", s[i]);
}
void re_l() {
int i, x = 0, k = 1, L = 0, fl, o;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar());
for (; c >= '0' && c <= '9'; c = getchar()) {
if (!(L - 1) && !aux[L])
L--;
aux[++L] = c - '0';
}
CL();
l = L / 8 + ((o = L % 8) > 0);
for (i = 1; i <= o; i++)
x = x * 10 + aux[i];
if (o)
s[l] = x;
fl = !o ? l + 1 : l;
for (i = o + 1, x = 0; i <= L; i++, k++) {
x = x * 10 + aux[i];
if (!(k ^ 8)) {
s[--fl] = x;
x = k = 0;
}
}
if (!l)
l = 1;
}
ll toint() // transfer to integer
{
ll x = 0;
for (int i = l; i; i--)
x = x * base + s[i];
return x;
}
bigint operator=(int b) {
CL();
do {
s[++l] = b % base;
b /= base;
} while (b > 0);
return *this;
}
bigint operator=(ll b) {
CL();
do {
s[++l] = b % base;
b /= base;
} while (b > 0);
return *this;
}
bigint operator+(const int &b) {
bigint c = *this;
ll x = b;
for (int i = 1; i <= l && x; i++) {
x = x + c.s[i];
c.s[i] = x % base;
x /= base;
}
if (x)
c.s[++c.l] = x;
return c;
}
bigint operator+(const ll &b) {
bigint c = *this;
ll x = b;
for (int i = 1; i <= l && x; i++) {
x = x + c.s[i];
c.s[i] = x % base;
x /= base;
}
if (x)
c.s[++c.l] = x;
return c;
}
bigint operator+(bigint &b) {
if (b.l < 3)
return *this + b.toint();
bigint c;
ll x = 0;
int k = l < b.l ? b.l : l;
c.CL();
c.l = k;
for (int i = 1; i <= k; i++) {
x = x + s[i] + b.s[i];
c.s[i] = x % base;
x /= base;
}
if (x)
c.s[++c.l] = x;
return c;
}
bigint operator-(const bigint &b) {
bigint c, d = *this;
ll x = 0;
c.CL();
for (int i = 1; i <= l; i++) {
if ((x = d.s[i]) < b.s[i]) {
d.s[i + 1]--;
x += base;
}
c.s[i] = x - b.s[i];
}
c.l = l;
for (; !c.s[c.l] && c.l > 1; c.l--);
return c;
}
bigint operator-(const int &b) {
bigint c;
return *this - (c = b);
}
bigint operator-(const ll &b) {
bigint c;
return *this - (c = b);
}
bigint operator*(const int &b) {
bigint c;
ll x = 0;
c.CL();
for (int i = 1; i <= l; i++) {
x = x + 1LL * s[i] * b;
c.s[i] = x % base;
x /= base;
}
for (c.l = l; x; x /= base)
c.s[++c.l] = x % base;
return c;
}
bigint operator*(bigint &b) {
if (b.l < 2)
return *this * b.toint();
bigint c;
ll x;
int i, j, k;
c.CL();
for (i = 1; i <= l; i++) {
x = 0;
for (j = 1; j <= b.l; j++) {
x = x + 1LL * s[i] * b.s[j] + c.s[k = i + j - 1];
c.s[k] = x % base;
x /= base;
}
if (x)
c.s[i + b.l] = x;
}
for (c.l = l + b.l; !c.s[c.l] && c.l > 1; c.l--);
return c;
}
bigint operator*(const ll &b) {
bigint c;
if (b > 2e9) {
c = b;
return *this * c;
}
ll x = 0;
c.CL();
for (int i = 1; i <= l; i++) {
x = x + b * s[i];
c.s[i] = x % base;
x /= base;
}
for (c.l = l; x; x /= base)
c.s[++c.l] = x % base;
return c;
}
bigint operator/(const int &b) {
bigint c;
ll x = 0;
c.CL();
for (int i = l; i; i--) {
c.s[i] = (x * base + s[i]) / b;
x = (x * base + s[i]) % b;
}
for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
return c;
}
bigint operator/(const ll &b) {
bigint c;
ll x = 0;
c.CL();
for (int i = l; i; i--) {
c.s[i] = (x * base + s[i]) / b;
x = (x * base + s[i]) % b;
}
for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
return c;
}
bigint operator/(bigint &b) {
if (b.l < 2)
return *this / b.toint();
bigint c, d;
int i, j, le, r, mid, k;
c.CL();
d.CL();
for (i = l; i; i--) {
for (j = ++d.l; j > 1; j--)
d.s[j] = d.s[j - 1];
d.s[1] = s[i];
if (d < b)
continue;
le = k = 0;
r = base - 1;
while (le <= r) {
mid = (le + r) >> 1;
if (b * mid <= d) {
le = mid + 1;
k = mid;
} else
r = mid - 1;
}
c.s[i] = k;
d = d - b * k;
}
for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
return c;
}
bigint operator%(const int &b) {
bigint c;
ll x = 0;
c.CL();
for (int i = l; i; i--)
x = (x * base + s[i]) % b;
return c = x;
}
bigint operator%(const ll &b) {
bigint c;
ll x = 0;
c.CL();
for (int i = l; i; i--)
x = (x * base + s[i]) % b;
return c = x;
}
bigint operator%(bigint &b) {
if (b.l < 2)
return *this % b.toint();
bigint c;
int i, j, le, r, mid, k;
c.CL();
for (i = l; i; i--) {
for (j = ++c.l; j > 1; j--)
c.s[j] = c.s[j - 1];
c.s[1] = s[i];
if (c < b)
continue;
le = k = 0;
r = base - 1;
while (le <= r) {
mid = (le + r) >> 1;
if (b * mid <= c) {
le = mid + 1;
k = mid;
} else
r = mid - 1;
}
c = c - b * k;
}
for (; !c.s[c.l] && c.l > 1; c.l--);
return c;
}
bigint operator+=(bigint &b) {
return *this = *this + b;
}
bigint operator+=(ll &b) {
return *this = *this + b;
}
bigint operator+=(int &b) {
return *this = *this + b;
}
bigint operator-=(bigint &b) {
return *this = *this - b;
}
bigint operator-=(ll &b) {
return *this = *this - b;
}
bigint operator-=(int &b) {
return *this = *this - b;
}
bigint operator*=(bigint &b) {
return *this = *this * b;
}
bigint operator*=(ll &b) {
return *this = *this * b;
}
bigint operator*=(int &b) {
return *this = *this * b;
}
bigint operator/=(bigint &b) {
return *this = *this / b;
}
bigint operator/=(ll &b) {
return *this = *this / b;
}
bigint operator/=(int &b) {
return *this = *this / b;
}
bigint operator%=(bigint &b) {
return *this = *this % b;
}
bigint operator%=(ll &b) {
return *this = *this % b;
}
bigint operator%=(int &b) {
return *this = *this % b;
}
bool operator<(const bigint &b) const {
if (l ^ b.l)
return l < b.l;
for (int i = l; i; i--)
if (s[i] ^ b.s[i])
return s[i] < b.s[i];
return false;
}
bool operator<=(const bigint &b) const {
if (l ^ b.l)
return l < b.l;
for (int i = l; i; i--)
if (s[i] ^ b.s[i])
return s[i] < b.s[i];
return true;
}
bool operator>(const bigint &b) const {
if (l ^ b.l)
return l > b.l;
for (int i = l; i; i--)
if (s[i] ^ b.s[i])
return s[i] > b.s[i];
return false;
}
bool operator>=(const bigint &b) const {
if (l ^ b.l)
return l > b.l;
for (int i = l; i; i--)
if (s[i] ^ b.s[i])
return s[i] > b.s[i];
return true;
}
bool operator==(const bigint &b) const {
if (l ^ b.l)
return false;
for (int i = l; i; i--)
if (s[i] ^ b.s[i])
return false;
return true;
}
bool operator!=(const bigint &b) const {
if (l ^ b.l)
return true;
for (int i = l; i; i--)
if (s[i] ^ b.s[i])
return true;
return false;
}
bool operator<(ll b) const {
bigint c;
return *this < (c = b);
}
bool operator<=(ll b) const {
bigint c;
return *this <= (c = b);
}
bool operator>(ll b) const {
bigint c;
return *this > (c = b);
}
bool operator>=(ll b) const {
bigint c;
return *this >= (c = b);
}
bool operator==(ll b) const {
bigint c;
return *this == (c = b);
}
bool operator!=(ll b) const {
bigint c;
return *this != (c = b);
}
bool operator<(int b) const {
bigint c;
return *this < (c = b);
}
bool operator<=(int b) const {
bigint c;
return *this <= (c = b);
}
bool operator>(int b) const {
bigint c;
return *this > (c = b);
}
bool operator>=(int b) const {
bigint c;
return *this >= (c = b);
}
bool operator==(int b) const {
bigint c;
return *this == (c = b);
}
bool operator!=(int b) const {
bigint c;
return *this != (c = b);
}
};
bigint ksm(bigint x, bigint y, bigint md) {
bigint s;
s = 1;
for (; y > 0; y = y / 2, x = x * x % md)
if (y.s[1] & 1)
s = s * x % md;
return s;
}
bigint n,modd,ans,tmp,tmp2,tmp3;
signed main() {
n.re_l();
ans=1;
modd=1;
int c10=10;
int k; scanf("%d", &k);
for(int i=1;i<=k;++i){
modd*=c10; // mod
tmp=n%modd;
tmp2 = ksm(tmp,ans,modd);
tmp3 = tmp2*tmp%modd;
// tmp2.pr();putchar(10);
// tmp3.pr();putchar(10);
int xhj = 1;
while(tmp3!=tmp){
tmp3 = tmp3*tmp2%modd;
// tmp3.pr();
// putchar(10);
xhj++;
// 不存在(比如10 10)
if(xhj>30) {
printf("-1\n");
return 0;
}
}
// DB1(xhj);
ans *= xhj;
}
ans.pr();
}
洛谷 打砖块
首先观察到,如果我们当时正在打的子弹是最后一颗子弹,那么打掉一个N后,就算下一个砖块是Y,我们也没有子弹去打他。因此,我们把情况分为:当前子弹是最后一颗子弹,当前子弹不是最后一颗子弹。
那么,考虑 [ 1 , i − 1 ] [1,i-1] [1,i−1] 列和第 i i i 列,情况就可以分为:
(1)第 i i i 列一个也不打,dp直接继承。
(2)最后一颗子弹在第 i i i 列被打掉。
(3)最后一颗子弹在第 [ 1 , i − 1 ] [1,i-1] [1,i−1] 列被打掉。
(4)最后一颗子弹不在 [ 1 , i ] [1,i] [1,i] 列被打掉。(那么这一颗必然在 [ i + 1 , n ] [i+1,n] [i+1,n]中。但我们只考虑到第i列所以不管)
这些情况是完备的。
于是我们可以定义状态:
d p [ i ] [ j ] [ 0 ] dp[i][j][0] dp[i][j][0]:到第i列,花了j个子弹,最后一颗子弹在 [ 1 , i ] [1,i] [1,i]中,最大的收益。
d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]:到第i列,花了j个子弹,最后一颗子弹不在 [ 1 , i ] [1,i] [1,i]中,最大的收益。
定义辅助数组:
s u m [ i ] [ j ] [ 0 ] sum[i][j][0] sum[i][j][0]:打第i列,打到第j个砖块,刚好打完子弹,后面有Y也打不成(即最后一颗子弹打在这一列)的收益。
s u m [ i ] [ j ] [ 1 ] sum[i][j][1] sum[i][j][1]:打第i列,打到第j个砖块,后面有Y就继续推(打这一列的时候子弹充足),的收益。
t o t [ i ] [ j ] tot[i][j] tot[i][j]:打第i列,达到第j个砖块,花费的子弹数目。
那么转移就可以写成:
// 第i列一个也不打,直接继承
dp[i][j][0]=dp[i-1][j][0];
dp[i][j][1]=dp[i-1][j][1];
// 最后一颗子弹在[1,i-1]中
dp[i][j][0]=dp[i-1][j-tot[i][k]][0]+sum[i][k][1];
// 最后一颗子弹在i中
dp[i][j][0]=dp[i-1][j-tot[i][k]][1]+sum[i][k][0];
// 最后一颗子弹不在[1,i]中
dp[i][j][1]=dp[i-1][j-tot[i][k]][1]+sum[i][k][1];
//
// Created by Artist on 2021/8/31.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
template<class T>void ChkMax(T &a,T b){a=a<b?b:a;}
const int INF=0x3f3f3f3f;
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
const int inf = 0x3f3f3f3f;
const int maxn = 203;
ll f[maxn][maxn],c[maxn][maxn];
ll sum[maxn][maxn][2];//0:最后一颗子弹在i,1:最后一颗子弹不在i
ll dp[maxn][maxn][2];
int cur[maxn],tot[maxn][maxn];
signed main() {
// 行数,列数
int n,m,K;scanf("%d%d%d",&n,&m,&K);
for(int i=n;i;--i){
for(int j=1;j<=m;++j){
scanf("%lld",&f[j][i]);
char ch[4];scanf("%s",ch);
if(ch[0]=='Y') c[j][i]=1;
else c[j][i]=0;
}
}
ll ans=0;
// 用一颗子弹把前面的Y全部打掉
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
cur[i]=j;
if(!c[i][j]) break;
ans += f[i][j];
}
}
// 预处理sum数组、tot数组
for(int i=1;i<=m;++i){
// nxt:把Y全部压掉,跑的都是N
for(int j=cur[i],nxt;j<=n;j=nxt){
tot[i][j]++;
nxt=j+1;
sum[i][j][0]+=f[i][j];
sum[i][j][1]+=f[i][j];
ll sm=0;
while(nxt<=n && c[i][nxt]) sm+=f[i][nxt],nxt++;
sum[i][j][1]+=sm;
if(nxt>n) break;
sum[i][nxt][0]+=sum[i][j][0];
sum[i][nxt][1]+=sum[i][j][1];
tot[i][nxt]+=tot[i][j];
}
}
for(int i=0;i<=m;++i) dp[i][0][0]=-inf;
// 第几列
for(int i=1;i<=m;++i){
// 背包容量
for(int j=1;j<=K;++j){
dp[i][j][0]=dp[i-1][j][0];
dp[i][j][1]=dp[i-1][j][1];
// 当前这列打多少个砖块
for(int k=cur[i];k<=n;++k){
if(!c[i][k] && j-tot[i][k]>=0) {
dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-tot[i][k]][0]+sum[i][k][1]);
dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-tot[i][k]][1]+sum[i][k][0]);
dp[i][j][1]=max(dp[i][j][1],dp[i-1][j-tot[i][k]][1]+sum[i][k][1]);
}
}
}
}
printf("%lld\n",dp[m][K][0]+ans);
}