>>>这场比赛的链接
题意:t组数据,每组给出一个n,询问1~n中各个位上都相同的数的个数,比如1~10有1 2 3 4 5 6 7 8 9这九个数满足,答案是9。
思路:1~9,10~99,100~999,...各自都有9位,所以获取一下最高位的位数,最高位特殊处理一下+(最高位的位数-1)*9就是答案了。
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
const int N = 1e5+5;
int main()
{
duozu{
int n;
R(n);
int num=0,t = n;
while (t) num++,t/=10;
int mod = 1;
for1(i,1,num-1) mod*=10;
int high = n/mod;
int bord = 0;
for1(i,1,num) bord = bord*10+high;
int ans = 9*(num-1);
//W("n=",n,"bord=",bord,"num=",num,"high=",high);
if (n>=bord) ans += high;
else ans += high-1;
W(ans);
}
return 0;
}
官方题解的过程更清晰,通过找出每个数和n进行比较
cin >> n;
int b = 0, ans = 0;
for (int len = 1; len <= 9; len++) {
b = b * 10 + 1;
for (int m = 1; m <= 9; m++)
if (b * m <= n)
ans++;
}
cout << ans << endl;
题意:t组数据,每组n个数,每次操作可以选取一个偶数x,会将这n个数中所有等于x的都/2,询问最少多少次可以将数列中的数全部变为奇数。
思路:首先一定是先从大的偶数开始/2,这样更有利于聚集更多相同偶数一起/2,因此我们对n个数降序排序,然后我们记录当前的数变为奇数需要多少次,然后标记途中的中间数,这些数如果等会与之后进行操作的n个数中的数相等,那么那个数就不需要进行操作就可以变成奇数了——因为之前已经处理过这个数变成奇数的过程。
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
const int N = 2e5+5;
mapmp;
int a[N];
bool cmp(int a,int b){return a>b;}
int main()
{
duozu{
mp.clear();
int n;
R(n);
for1(i,1,n) R(a[i]);
sort(a+1,a+1+n,cmp);
//for1(i,1,n) W(a[i]);
int ans = 0;
for1(i,1,n){
if (a[i]&1) continue;
if (mp.count(a[i])==0){
mp[a[i]] = 1;
while (a[i]%2==0){
a[i]/=2;
mp[a[i]] = 1;
ans++;
}
}
}
W(ans);
}
return 0;
}
题意:t组数据,每组给出一个串由twone五个字符任意数量组成,询问你最少删除几个字符以及这几个字符的位置使得串中不出现one和two(这几个字符同时删除)
思路:我们删掉的字母必须是作用最大的——即删除这个字母可以做到删掉其他好几个字母的事情。我用了看毛片KMP算法分别去one和two。
不论one还是two,我们都优先删掉o字母,因为o可以避免两种串的产生。
除非遇到...oone以及twoo...的情况,此时删掉n和w是最好的选择。
(代码中我把删掉的操作转化为把这个字符赋值为‘1’,这样仿佛会存在的问题是,我匹配完one,然后原字符串并没有实际的删减而导致漏匹配一些two的情况——这种情况是不存在的因为我们处理完one之后的串中有o1e,..oo1e两种情况,很显然没有截断任何有价值的情况)
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
const int N = 150000+5;
char s[N];
int fail[N];
char ss[2][10]={"one","two"};
int ans;
int pos[N],idx;
void kmp(char* s,char* ss){
int len = strlen(ss);
fail[0] = -1;
for (int i=0,j=-1;i=0 && s[i-4]!='o') s[i-3] = '1',pos[ans++]=i-3;
else s[i-2]='1',pos[ans++] = i-2;
}
}
}
}
int main()
{
duozu{
R(s);
ans = idx = 0;
kmp(s,ss[0]);
kmp(s,ss[1]);
sort(pos,pos+ans);
W(ans);
for0(i,0,ans){
if (i!=0) printf(" ");
printf("%d",pos[i]+1);
}puts("");
}
return 0;
}
题意:t组数据,每组n个01串。
1.串a串b可连接成ab的条件是a的末尾 = b的开头
2.我们可以进行的操作有翻转一个串
3.询问最少多少次可以把所有串连接并给出这些串的位置
4.最终不允许有重复串(当然保证初始状态没有重复不然做个P)
思路:用开头结尾分类成四种:01,10,11,00
我们发现00,11根本没啥用,只要有01或者10,存在11,00必定可以连上去——除非11,00同时存在且没有01或者10
排除上面那种状况后,我们假设01有a个,10有b个
怎样才能连成成一条取决于a,b的数量
我们发现|a-b| = 0 或者 1时可以连接起来,直接抽象来说——一种黑砖一种白砖间隔着拼起来那么可以ababa,也可以abab。
所以我们将多的那一种瓷砖转化|a-b|/2块。
麻烦的地方在于不能重复...
我的思路是黑白砖头各自开一个map
然后其中一个存反串,这样就能直接判断这个串的反串是否在另一个串中出现。
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
const int N = 4e6+5;
char s[N],ss[N];
int p01[N],p10[N];
int ans[N];
unordered_mapmp01,mp10;
int main()
{
duozu{
mp01.clear();
mp10.clear();
int n;
R(n);
int a01=0,a10=0,a11=0,a00=0;
for1(i,1,n){
R(s);
int len = strlen(s);
if (s[0]=='0' && s[len-1]=='0') a00++;
if (s[0]=='0' && s[len-1]=='1') {p01[a01++] = i;mp01.count(s)==0?mp01[s] = i:mp01[s]++;}
if (s[0]=='1' && s[len-1]=='0') {p10[a10++] = i;for0(i,0,len)ss[i] = s[len-i-1];ss[len] = '\0';mp10.count(ss)==0?mp10[ss] = i:mp10[ss]++; }
if (s[0]=='1' && s[len-1]=='1') a11++;
}
if (a01==0 && a10==0 && a00!=0 && a11!=0) puts("-1");
else {
int num = abs(a01-a10)/2;
int idx = 0;
if (a01>a10){//找到num个01中翻转后不存在与10中的串(mp10.count(s)==0)
for (auto it = mp01.begin();it!=mp01.end();it++){
//cout << it->fi << endl;
if (mp10.count(it->fi)==0){
ans[idx++] = it->se;
}
}
}
else if (a01fi)==0){
ans[idx++] = it->se;
}
}
}
if (idx>=num){
W(num);
for (int i=0;i
题意:一个图中有ab两点,询问必须经过ab才能到达的点的对数。
思路:染色两边,将只能过a的点染为1,只能过b的点染为2,同时能过的染为3,答案等于1的数量*2的数量
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
const int N = 2e5+5;
const int M = 5e5+5;
struct E{
int to,last;
}edge[M<<1];
int id,head[N];//双向建边记得X2
void add(int u,int v){edge[id].to = v;edge[id].last = head[u];head[u] = id++;}
int col[N];
bool first;
void dfs(int now,int cc,int out){
if (first) first = false;
else {
if (col[now]>=out) return ;
col[now] += cc;
}
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
dfs(v,cc,out);
}
}
int main()
{
int n,m,a,b;
int c1,c2;
duozu{
R(n,m,a,b);
id = 1;
for1(i,1,n) head[i] = 0,col[i] = 0;
c1 = c2 = 0;
int u,v;
for1(i,1,m) R(u,v),add(u,v),add(v,u);
col[a] = col[b] = 4;
first = true,dfs(a,1,1);
first = true,dfs(b,2,2);
for1(i,1,n){
//W(i,"=",col[i]);
if (col[i]==1) c1++;
else if (col[i]==2) c2++;
}
W(1LL*c1*c2);
}
return 0;
}
题意:给出n个数,进可能多的利用这些数,拼出一个矩形,要求每一行每一列元素不能重复
思路:需要先发现一些规律,我们假定矩形为r*c:c行,每行r个(我当时定义反了所以现在也这么说无所谓啦)
并且规定r 我们把数据处理一下后,维护一个升序的记录每种数数量的数组,再维护一下前缀和 因此,第一维O(N)枚举r,然后二分查找一下第一个大于等于r的位置p,可以利用的数的数量为前p-1个的前缀个+剩余的每个利用r个,这个数量/r就是c的大小。 枚举完之后我们获取最大情况下的r和c然后接下去利用上面所说的方法去铺就行了。 加油 #include